Comment by em-bee

3 months ago

unfortunately i hit another snag: https://stackoverflow.com/questions/3884927/how-to-use-xsl-v...

nodes you output don't have type "node-set" - instead, they're what is called a "result tree fragment". You can store that to a variable, and you can use that variable to insert the fragment into output (or another variable) later on, but you cannot use XPath to query over it.

the xsl documentation https://www.w3.org/TR/xslt-10/#variables says:

Variables introduce an additional data-type into the expression language. This additional data type is called result tree fragment. A variable may be bound to a result tree fragment instead of one of the four basic XPath data-types (string, number, boolean, node-set). A result tree fragment represents a fragment of the result tree. A result tree fragment is treated equivalently to a node-set that contains just a single root node. However, the operations permitted on a result tree fragment are a subset of those permitted on a node-set. An operation is permitted on a result tree fragment only if that operation would be permitted on a string (the operation on the string may involve first converting the string to a number or boolean). In particular, it is not permitted to use the /, //, and [] operators on result tree fragments.

so using apply-templates on a variable doesn't work. this is actually where i got stuck before. i just was not sure because i could not verify that everything else was correct.

i wonder if it is possible to load the menu from a second document: https://www.w3.org/TR/xslt-10/#document

edit: it is!

    <xsl:apply-templates select="document('nav-menu.xml')/menu">

now i just need to finetune this because somehow the $current param fails now.

Ah, I could've sworn that it worked in some version of the page that I tried as I iterated on things, but it could be that the browser just froze on my previously working page and I fooled myself.

Adding xmlns:exsl="http://exslt.org/common" to your xsl:stylesheet and doing select="exsl:node-set($nav-menu-items)/item" seems to work on both Chrome and Librewolf.

  • tried that, getting an empty match.

    here is the actual stylesheet i am using:

        <?xml version="1.0" encoding="UTF-8"?>
        <xsl:stylesheet version="1.0" xmlns:exsl="http://exslt.org/common" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
          <xsl:output method="html"/>
    
          <xsl:variable name="nav-menu">
            <item href="/">Home</item>
            <item href="/about.xhtml">About</item>
          </xsl:variable>
    
          <xsl:template match="document">
            <html>
              <head>
                <meta charset="utf-8" />
                <title><xsl:value-of select="title" /></title>
                <link rel="stylesheet" type="text/css" href="site.css" />
              </head>
    
              <body>
                <!-- <xsl:apply-templates select="document('nav-menu.xml')/menu"> -->
                <xsl:apply-templates select="exsl:node-set($nav-menu)/item">
                  <xsl:with-param name="current" select="@name"/>
                </xsl:apply-templates>
                <xsl:apply-templates select="content" />
              </body>
            </html>
          </xsl:template>
    
          <xsl:template match="item">
            <xsl:param name="current"/>
            <xsl:choose>
              <xsl:when test="@href=$current">
                <a class="selected"><xsl:apply-templates/></a>
              </xsl:when>
              <xsl:otherwise>
                <a href="{@href}"><xsl:apply-templates/></a>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:template>
    
          <xsl:template match="content">
            <xsl:apply-templates select="@*|node()" />
          </xsl:template>
    
          <xsl:template match="@*|node()">
            <xsl:copy>
              <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
          </xsl:template>
        </xsl:stylesheet>
    
    

    documents look like this:

        <?xml version="1.0" encoding="UTF-8" ?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <?xml-stylesheet type="text/xsl" href="site.xsl"?>
        <document name="about">
          <title>About Us</title>
          <content>
            html content here, to be inserted without change
          </content>
        </document>
    

    if i use the document() function, with nav-menu.xml looking like this:

        <menu>
          <item href="/">Home</item>
          <item href="/about.xhtml">About</item>
        </menu>
    

    then i get the menu items, but the test <xsl:when test="@href=$current"> fails

    • It looks like it's related to your setting the default namespace xmlns="http://www.w3.org/1999/xhtml". You could either add a xmlns:example="http://example.org/templates" and then replace `item` with `example:item` everywhere, or you can override the default namespace within your variable's scope:

          <xsl:variable name="nav-menu-items" xmlns="">
              <item href="/">Home</item>
              <item href="/about.xhtml">About</item>
          </xsl:variable>
      

      I think you also don't really need to set the default namespace to xhtml, so I believe you could remove that and not worry about namespaces at all (except for xsl and exsl).

      The test is failing because it's `/about.xhtml` in the template but `about` outside. You'd either need to add a name attribute to item to compare on or make it match the href.

      That should make your thing work if I haven't fooled myself again. :)

      2 replies →