Search

Ended up putting this together yesterday. It works pretty well in case anyone might need it.
Not putting into the XSLT Utilities yes, as it's still fairly untested.

Comments/suggestions hugely appreciated.

Overview

You have an element in the HTML you are parsing for which you would like to update some attributes. This helper does that. To use it match your element like this:

<xsl:template match="div" mode="template-engine">
...

Do you stuff, set your conditions on which you'd like to change attributes etc., then continue like this:

    ...
    <xsl:apply-templates select="." mode="add-attributes">
        <xsl:with-param name="new-attributes">
            <class>active</class>
            <class>selected</class>
            <style>color: red; background-color: green; height:100px;</style>
        </xsl:with-param>
    </xsl:apply-templates>
</xsl:template>

The result will be a replaced element, with the new attribute's values merged with the existing ones. So if you create a new class="" attribute, it will be added without replacing the already existing ones.

Params

  • new-attributes: node-set() of the new attribute names and values
  • element: lets you change the element name. Cherry on top.

    <xsl:template match="*" mode="add-attributes">
        <xsl:param name="this" select="."/>
        <xsl:param name="new-attributes"/>
        <xsl:param name="all-attributes">
            <xsl:apply-templates select="@*" mode="collect-existing-attributes"/>
            <xsl:for-each select="exsl:node-set($new-attributes)/*">
                <xsl:element name="{name()}">
                    <xsl:value-of select="."/>
                </xsl:element>
            </xsl:for-each>
        </xsl:param>
        <xsl:param name="element" select="name()"/>
    
        <xsl:variable name="merged-attributes">
            <xsl:call-template name="merge-attributes">
                <xsl:with-param name="attributes" select="$all-attributes"/>
            </xsl:call-template>
        </xsl:variable>
    
        <!-- Create new element with updated attributes, then continue in "template-engine" mode -->
        <xsl:element name="{$element}">
            <xsl:for-each select="exsl:node-set($merged-attributes)/*">
                <xsl:attribute name="{name()}">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:for-each>
    
            <xsl:apply-templates select="* | text()" mode="template-engine">
                <xsl:with-param name="this" select="$this"/>
            </xsl:apply-templates>
        </xsl:element>
    </xsl:template>
    
    <!-- collects existing attributes into a node-set like: <name>value</name> -->
    <xsl:template match="@*" mode="collect-existing-attributes">
        <xsl:element name="{name(.)}">
            <xsl:value-of select="." />
        </xsl:element>
    </xsl:template>
    
    <!-- Sift through all the attributes found and merge the values of nodes with the same name -->
    <xsl:template name="merge-attributes">
        <xsl:param name="attributes"/>
    
        <xsl:variable name="return">
            <xsl:for-each select="exsl:node-set($attributes)/*">
                <xsl:if test="name() != name(preceding-sibling::node()[1])">
                    <xsl:element name="{name()}">
                        <xsl:for-each select="exsl:node-set($attributes)/*[name() = name(current())]">
                            <xsl:choose>
                                <xsl:when test="position() = last()">
                                    <xsl:value-of select="node()"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select=" concat(node(), ' ')"/>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:for-each>
                    </xsl:element>
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>
    
        <xsl:copy-of select="exsl:node-set($return)"/>
    </xsl:template>
    

Sorry for the Markdown disaster. 4 spaces weren't enough (?). Fixed.

If a list item is followed by code, Markdown assumes that you want paragraphs in your list item. You would use the same indenting syntax for this:

* Sometimes this feature is welcome.

    Because your list item tells a longer story.
  • Sometimes this feature is welcome.

    Because your list item tells a longer story.

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details