Navigation in the content node tree is done using xpath features.
XPath is language for navigating and addressing parts of an xml
document. All nodes can be addressing using xpath syntax, some
construct gets really long and hard to read, but it is quite
effective once you get the hang of it.
Lets look at an example from the Umbraco
RunwayTopNavigation.xslt:
Example
...
<xsl:for-each select="$currentPage/ancestor-or-self::node [@level=$level]/node [string(data [@alias='umbracoNaviHide']) != '1']">
...
This line describes that we iterate over a set of
nodes specified by an xpath expression. To
descipher the XPath lets take it bit by bit. Generally you read the
expression from left to right, continously keeping track of what
nodeset we are are working with.
- $currentPage
- We start out with the current context page node $currentPage -
this is our bases. Note that "node" in the expression is actually
an element called "node"
- /ancestor-or-self::node
- From here we find any ancestor node-element as well as our
current node (self)
- [@level=$level]
- We filter all found nodes by defining that we only want the
nodes that are on the right level. @level is an attribute
of all node-elements holding the level of the node, meaning
the level of descend from the root. $level is a variable holding
our fixed level number.
- node [string(data [@alias='umbracoNaviHide']) != '1']
- From here we take all "node"-children with a data-child of our
nodeset (all "node"-elements in Umbraco holds a data-child
node) where the @alias attribute called "umbracoNaviHide" (which is
how custom properties of the document type are referred to) is
not set to true (i.e. equals "1")
The result is a set of nodes at a specified level which are not
hidden.
What's the deal with the []-predicates ?
Often you see one or more [expr]-parts being used in XPath
expressions. These are really interesting, because they are
powerfull to us, not least when using xslt in Umbraco. When
interpreting XPath expressions we normally navigate around the
content tree using siblings, descendent and other node-navigating
terms. Once we find the right type of node, we sometimes want to
filter them based on specific tests on attribute values or similar.
This is exactly what we use the [] for. The [expr] is called a
predicate, and the predicate expression
always evaluates to true or false, where true
values indicates accepted predicate. In the above
example we had [@level=$level] - this evaluates to true only
when the @level attribute equals our $level attribute. Multiple
predicates can be applied in the same expression - also for the
same nodes, which is really useful when accessing document
properties.
Say you want to iterate over all children of the current node
and filter them on some specific document property, instead of do a
for-each on all children and then inside do an if-statement testing
for the specific document property you can do like this:
<xsl:for-each select="$currentPage/node/data [@alias = 'mySpecialProperty'][string(.) != '']">
In this case we want to use data-nodes with @alias
= 'mySpecialProperty' - which is how content properties
are referenced. Since the content of this data-node element is
the mySpecialProperty-property value and we want to test on
that we convert it to a string ( string(.) ) and test if it is
empty - a simple test in this case.
XPath, unabbreviated syntax
Here are a few samples from the w3c xpath site:
Here are some examples from the w3c-site of XPath
expressions using the unabbreviated syntax:
child::para |
selects the para element children of the context
node |
child::* selects |
all element children of the context node |
child::text() |
selects all text node children of the context node |
child::node() |
selects all the children of the context node, whatever their
node type |
attribute::name |
selects the name attribute of the context node |
attribute::* |
selects all the attributes of the context node |
descendant::para |
selects the para element descendants of the context node |
ancestor::div |
selects all div ancestors of the context node |
ancestor-or-self::div |
selects the div ancestors of the context node and, if the
context node is a div element, the context node as well |
descendant-or-self::para |
selects the para element descendants of the context node and,
if the context node is a para element, the context node as
well |
self::para |
selects the context node if it is a para element, and otherwise
selects nothing |
child::chapter/descendant::para |
selects the para element descendants of the chapter element
children of the context node |
child::*/child::para |
selects all para grandchildren of the context node |
/ |
selects the document root (which is always the parent of the
document element) |
/descendant::para |
selects all the para elements in the same document as the
context node |
/descendant::olist/child::item |
selects all the item elements that have an olist parent and
that are in the same document as the context node |
child::para[position()=1] |
selects the first para child of the context node |
child::para[position()=last()] |
selects the last para child of the context node |
child::para[position()=last()-1] |
selects the last but one para child of the context node |
child::para[position()>1] |
selects all the para children of the context node other than
the first para child of the context node |
following-sibling::chapter[position()=1] |
selects the next chapter sibling of the context node |
preceding-sibling::chapter[position()=1] |
selects the previous chapter sibling of the context node |
/descendant::figure[position()=42] |
selects the forty-second figure element in the document |
/child::doc/child::chapter[position()=5]/child::section[position()=2] |
selects the second section of the fifth chapter of the doc
document element |
child::para[attribute::type="warning"] |
selects all para children of the context node that have a type
attribute with value warning |
child::para[attribute::type='warning'][position()=5] |
selects the fifth para child of the context node that has a
type attribute with value warning |
child::para[position()=5][attribute::type="warning"] |
selects the fifth para child of the context node if that child
has a type attribute with value warning |
child::chapter[child::title='Introduction'] |
selects the chapter children of the context node that have one
or more title children with string-value equal to
Introduction |
child::chapter[child::title] |
selects the chapter children of the context node that have one
or more title children |
child::*[self::chapter or
self::appendix] |
selects the chapter and appendix children of the context
node |
child::*[self::chapter or
self::appendix][position()=last()] |
selects the last chapter or appendix child of the context
node |
XPath, abbreviated syntax
Here are some examples from w3c of location paths using
abbreviated syntax:
para |
selects the para element children of the context
node |
* |
selects all element children of the context node |
text() |
selects all text node children of the context node |
@name |
selects the name attribute of the context node |
@* |
selects all the attributes of the context node |
para[1] |
selects the first para child of the context node |
para[last()] |
selects the last para child of the context node |
*/para |
selects all para grandchildren of the context node |
/doc/chapter[5]/section[2] |
selects the second section of the fifth chapter of the
doc |
chapter//para |
selects the para element descendants of the chapter element
children of the context node |
//para |
selects all the para descendants of the document root and thus
selects all para elements in the same document as the context
node |
//olist/item |
selects all the item elements in the same document as the
context node that have an olist parent |
. |
selects the context node |
.//para |
selects the para element descendants of the context node |
.. |
selects the parent of the context node |
../@lang |
selects the lang attribute of the parent of the context
node |
para[@type="warning"] |
selects all para children of the context node that have a type
attribute with value warning |
para[@type="warning"][5] |
selects the fifth para child of the context node that has a
type attribute with value warning |
para[5][@type="warning"] |
selects the fifth para child of the context node if that child
has a type attribute with value warning |
chapter[title="Introduction"] |
selects the chapter children of the context node that have one
or more title children with string-value equal to
Introduction |
chapter[title] |
selects the chapter children of the context node that have one
or more title children |
employee[@secretary and @assistant] |
selects all the employee children of the context node that have
both a secretary attribute and an assistant attribute |