Re: [xsl] Retrieving top section from a navigation tree

Subject: Re: [xsl] Retrieving top section from a navigation tree
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Wed, 27 Oct 2004 13:17:54 -0400
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[@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[@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[@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@xxxxxxxxxxxxxxxx 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 ======================================================================

Current Thread