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