Hi Mario,
At 12:30 PM 10/27/2004, you wrote:
I have a navigation tree and would like to find out which main navigation
section I am located underneath when i have the unique id of the page I am
viewing.
Here's an example tree;
<xsl:variable name="ul">
<ul id="menu">
<li id="1">one</li>
<li id="2">two
<ul>
<li id="2-1">two-one</li>
<li id="2-2">two-two
<ul>
<li id="2-2-1">two-two-one</li>
</ul>
</li>
</ul>
</li>
<li id="3">three</li>
</ul>
</xsl:variable>
<xsl:variable name="id" select=="'2-2-1'" />
Say I am viewing the page "2-2-1", I would now like to write an xpath
expression that retrieves the name (text element) of the containing top li
element. In this case the name would be "two".
I have tried to look at the problem from differen perspectives;
- one is to find the top <li> elements and filter out any that does not
contain an <li> element that matches the page $id
- the other is to find the <li> element with the specific @id, and
traverse outwards to find the containing top <li> element
I like the second approach if using brute force:
exsl:node-set($ul)//li[(_at_)id=$id]/ancestor-or-self::li[not(ancestor::li)]/text()[1]
but if you want to optimize retrieval with a key, the first approach would
also work:
<xsl:key name="containing-li-by-ids" match="li[not(ancestor::li)]"
use=".//@id"/>
and then, given a context node of exsl:node-set($ul) or in the stylesheet
itself
key('containing-li-by-ids',$id)/text()[1]
Note I am taking only the first text node child, not all of them (which in
your example would include some whitespace). If it were my design I'd mark
up the name as such (in an element like <name> or hanging it on an
attribute) to make this more transparent and controllable.
Which approach is better would depend on the tradeoff between how large the
menu is (in normal cases I bet the advantage of the key is negligible) vs.
how much of a pain it is to manage the context so you can actually use a
key to retrieve a node appearing in your stylesheet, not in your source.
Another idea, if this numbering system ('2', '2-2-1' etc.) is what you
actually have, and is dependable, would be to cheat and just say
exsl:node-set($ul)//li[(_at_)id=substring-before($id,'-')]
although I'd prefer to do it for real and not introduce this dependency.
Also, in this case you can get around the need for exsl:node-set() by saying
<xsl:variable name="menu-tree"
select="document('')/*/xsl:variable[(_at_)name='ul']/ul"/>
since document('') returns the stylesheet -- thereby giving you $menu-tree
to work with instead of having to call the extension funtion all the time.
But note, none of this is tested: watch out for syntax errors.
Cheers,
Wendell
======================================================================
Wendell Piez
mailto:wapiez(_at_)mulberrytech(_dot_)com
Mulberry Technologies, Inc. http://www.mulberrytech.com
17 West Jefferson Street Direct Phone: 301/315-9635
Suite 207 Phone: 301/315-9631
Rockville, MD 20850 Fax: 301/315-8285
----------------------------------------------------------------------
Mulberry Technologies: A Consultancy Specializing in SGML and XML
======================================================================