Change div class in template based on metadata selection

vruppert's Avatar

vruppert

17 Dec, 2010 04:17 PM

I'm trying to change the class attribute on a div located in a Template depending on a metadata value set by the user. I cannot simply use a current page index block and format for this because this div is a sort of container div which contains the majority of html (including multiple other system-regions) on the template. I've been looking closely at this topic: http://help.hannonhill.com/discussions/how-do-i/19-system-tag-as-ht... and it seems to solve my problem however I'm using strictly Velocity and I guess I don't understand XSLT enough to get this to work.

In the linked to topic, a class attribute is added to the body tag by using this line: match="node()[name()='body']". How can I convert that to instead match a div with a specific ID? I've tried obvious combinations like //div[@id='foo'] without success. Does the initial match="/*" have anything to do with it? Is there a Velocity or easier way to do this?

  1. 1 Posted by Charlie Holder on 20 Dec, 2010 03:47 PM

    Charlie Holder's Avatar

    Currently there is no way to apply a Velocity Format to an entire Template. The best way for you to achieve this would probably be to do what the other topic you're referencing is doing.

    The XSLT listed in the other topic is a good start and with a little modification, you could apply it to a Configuration Set for your Page.

    I have made the necessary modifications to the XSLT format that I think should solve your issue.

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <!-- We get the value of the dynamic metadata field and assign it to a variable. -->
        <xsl:variable name="metedataValue" select="//calling-page/system-page/dynamic-metadata[name='YOUR-FIELD-NAME']/value"/>
    
        <xsl:template match="/*">
            <xsl:copy>
                <xsl:apply-templates/>
                <xsl:copy-of select="@*"/>
            </xsl:copy>
        </xsl:template>
    
        <!-- Here we match the XHTML div node wherethe ID attribute equals what you're looking for. -->
        <xsl:template match="node()[name()='div'][@id='YOUR-DIV-ID']">
    
        <xsl:choose>
            <!-- If the value of the metadata field equals your first option,
            set the class to something. -->
            <xsl:when test="$metedataValue = 'YOUR-OPTION-ONE'">
                <xsl:variable name="classToSet" select="YOUR-VALUE-ONE"/>
            </xsl:when>
    
            <!-- If the value of the metadata field equals your second option,
            set the class to something else. -->
            <xsl:when test="$metedataValue = 'YOUR-OPTION-TWO'">
                <xsl:variable name="classToSet" select="YOUR-VALUE-TWO"/>
            </xsl:when>
        </xsl:choose>
    
        <xsl:copy>
            <!-- This is where we assign the class variable to the div node we found earlier. -->
            <xsl:attribute name="class"><xsl:value-of select="$classToSet"/></xsl:attribute>
            <xsl:copy-of select="@*"/>
            <xsl:copy-of select="./node()"/>
        </xsl:copy>
    
        </xsl:template>
            <xsl:template match="*">
            <xsl:copy-of select="."/>
        </xsl:template>
    </xsl:stylesheet>
    

    Now you only need to insert the appropriate values for what output you desire and apply the format to a Configuration Set.

    YOUR-FIELD-NAME is the name of the dynamic metadata field that you are interested in.
    YOUR-DIV-ID is the id of the XHTML div that you are interested in changing the class of. If this div does not currently have an id, it will make no difference for you to add one as long as you do not use an id that has any CSS associated with it. You could easily make the id="myMetadataTest" and be done.

    It sounds like you would like to set a class name based on the metadata values and not directly use the metadata value as the class name.
    YOUR-OPTION-ONE is one possible option for the metadata value.
    YOUR-VALUE-ONE is the corresponding class name you would like to set for OPTION-ONE.

    YOUR-OPTION-TWO is a second possible option for the metadata value.
    YOUR-VALUE-TWO is the corresponding class name you would like to set for OPTION-TWO.

    You can safely repeat that block of XSLT code if you require more that two options as well.

    I hope this helps get you going further.

  2. 2 Posted by vruppert on 20 Dec, 2010 04:48 PM

    vruppert's Avatar

    I can't seem to match the div using either of your examples. Even simplifying it using strings instead of variables isn't changing anything on page preview.

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates/>
            <xsl:copy-of select="@*"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="node()[name()='div'][@id='mainspace_2']">
        <xsl:copy>
            <xsl:attribute name="class">TEST</xsl:attribute>
            <xsl:copy-of select="@*"/>
            <xsl:copy-of select="./node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="*">
        <xsl:copy-of select="."/>
    </xsl:template>
    </xsl:stylesheet>
    

    I have however had success changing the div I want by doing this:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="*">
    <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:apply-templates/> 
    </xsl:copy> 
    </xsl:template> 
    
    <xsl:template match="@*"> 
    <xsl:copy-of select="."/> 
    </xsl:template> 
    
    <xsl:template match="@id[.='mainspace_2']"> 
    <xsl:attribute name="id">mainspace_2</xsl:attribute> 
    <xsl:attribute name="class">TEST</xsl:attribute> 
    </xsl:template> 
    </xsl:stylesheet>
    

    The only problem that still remains is I can't seem to get the dynamic metadata value. Charle in your example you access //calling-page, which I didn't know existed on a configuration set. Is there a way to see what XML does exist on a configuration set?

  3. 3 Posted by Charlie Holder on 20 Dec, 2010 05:37 PM

    Charlie Holder's Avatar

    Sorry for the bad XSLT before. That code wouldn't match the templates properly to find the div inside the HTML. The previous code block only searched and matched for direct children elements, so it would have done a bad job of finding a div that was nested inside multiple other elements.

    I spent some time testing this code with another member of the Services team and we've come up with another version:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <!-- We get the value of the dynamic metadata field and assign it to a variable. -->
        <xsl:variable name="metedataValue" select="//calling-page/system-page/dynamic-metadata[name='YOUR-FIELD-NAME']/value"/>
        <xsl:choose>
            <!-- If value equals first option, set class to something. -->
            <xsl:when test="$metedataValue = 'YOUR-OPTION-ONE'">
                <xsl:variable name="classToSet" select="YOUR-VALUE-ONE"/>
            </xsl:when>
    
            <!-- If value equals second option, set class to something else. -->
            <xsl:when test="$metedataValue = 'YOUR-OPTION-TWO'">
                <xsl:variable name="classToSet" select="YOUR-VALUE-TWO"/>
            </xsl:when>
        </xsl:choose>
    
        <xsl:template match="/*">
            <xsl:copy>
                <xsl:apply-templates/>
                <xsl:copy-of select="@*"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="node()">
            <xsl:copy>
                <xsl:if test="name()='div' and @id='mainspace_2'">
                    <xsl:attribute name="class"><xsl:value-of select="$classToSet"/></xsl:attribute>
                </xsl:if>
                <xsl:copy-of select="@*"/>
    
                <!-- Recursively loop through nodes until we find the correct div. -->
                <xsl:apply-templates select="./node()"/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    
  4. 4 Posted by vruppert on 20 Dec, 2010 07:33 PM

    vruppert's Avatar

    That seemed to work after switching the <xsl:if and <xsl:copy-of lines inside the xsl:template match="node()" area. Also, the xsl:choose block threw an error for being in an invalid area. Working code:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates/>
            <xsl:copy-of select="@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:if test="name()='div' and @id='mainspace_2'">
                <xsl:attribute name="class">TEST</xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="./node()"/>
        </xsl:copy>
    </xsl:template>
    </xsl:stylesheet>
    

    But I still can not access dynamic metadata. Is this possible from an XSLT format attached to a configuration set and not an index block?

  5. 5 Posted by Charlie Holder on 20 Dec, 2010 08:06 PM

    Charlie Holder's Avatar

    Hmmm. I guess the XSLT is running on the final HTML that is being generated from all of the blocks and formats.

    Using a current page block and another <system-region> tag, you could place a div after the body tag of your HTML that only contains the name of the class you want to set after you run your logic.

    <div id="hiddenVal" style="display:none;">className</div>
    

    And then instead of the if logic in the XSLT, just pull that value and add it.

    <xsl:variable name="classToSet" select=".//node()[name() = 'div'][@id = 'hiddenVal']"/>
    
  6. 6 Posted by vruppert on 21 Dec, 2010 09:07 PM

    vruppert's Avatar

    That is definitely a solution but I think overall this process is going to get messier and messier. Ultimately I've found a way to avoid having the class name be necessary at all. Thank you for your help, I now know more about how Cascade works and how XSLT formats can be applied to config sets and templates.

  7. Tim closed this discussion on 14 Jan, 2011 06:00 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac