xsl-list
[Top] [All Lists]

Re: Get Position of Node in Ancestor Context

2003-03-10 10:33:58
Hi Ted,

Answered my own question, but would still be interested in knowing
what others think of this solution...

<xsl:template match="input|select|textarea">
     <xsl:copy>
         <xsl:for-each select="@*">
             <xsl:copy />
         </xsl:for-each>
         <xsl:variable name="currElemId" select="@id" />
         <xsl:attribute name="tabindex"></xsl:attribute>
         <xsl:for-each  
select="ancestor::form//select|ancestor::form//input|ancestor::form// 
textarea">
             <xsl:if test="not(@type = 'hidden') or @type = 'submit'">
                 <xsl:if test="@id = $currElemId">
                     <xsl:attribute name="tabindex"><xsl:value-of  
select="position()" /></xsl:attribute>
                 </xsl:if>
             </xsl:if>
         </xsl:for-each>
         <xsl:apply-templates />
     </xsl:copy>
</xsl:template>

I think that you'd probably be better off using <xsl:number> here.
Try:

<xsl:template match="input | select | textarea">
  ...
  <xsl:attribute name="tabindex">
    <xsl:number from="form" level="any"
      count="*[self::input or self::select or self::textarea]
              [(_at_)type != 'hidden']" />
  </xsl:attribute>
  ...
</xsl:template>

You could use "input[(_at_)type != 'hidden'] | select[(_at_)type != 'hidden'] |
textarea[(_at_)type != 'hidden']" as the value of the count attribute if
you prefer. Note that I dropped the "or @type = 'select'" since any
element with a type attribute equal to 'select' will pass the "@type
!= 'hidden'" test.

The style sheet basically adds tabindex attributes to all form
elements. What I'm unhappy with is the value of the select attribute
of the second for-each element (the one that creates a node set of
all child elements of a FORM element, regardless of what separates
them). It seems odd that I have to fully qualify the child element
I'm looking for, but using ancestor::form//select|input|textarea
only matches the first element in the list.

Yes; in XPath 2.0, we'll be able to use:

  ancestor::form//(select | input | textarea)

which is a good deal shorter, and likewise with the count attribute
above, we'll be able to do:

  (input | select | textarea)[(_at_)type != 'hidden']

Also, what is the performance impact on setting up such for-each
loops and is there a faster way to reproduce this node-set in the
context of the current element (call-template with-param???)?

Numbering based on position within the node tree is always fairly
expensive because it can require a lot of node visits. If the <input>,
<select> and <textarea> elements are siblings, you can use
level="single", which will mean that the processor doesn't have to
look at quite as many nodes. Or you could use a step-by-step recursive
template and pass the current count on from sibling to sibling. But
unless you find performance is really suffering here, I'd stick with
the <xsl:number> solution above.

By the way, rather than:

  <xsl:for-each select="@*"><xsl:copy /></xsl:for-each>

you could just do:

  <xsl:copy-of select="@*" />

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



<Prev in Thread] Current Thread [Next in Thread>