XPath axis, get all following nodes until
Use:
(//h2[. = 'Foo bar'])[1]/following-sibling::p
[1 = count(preceding-sibling::h2[1] | (//h2[. = 'Foo bar'])[1])]
In case it is guaranteed that every h2
has a distinct value, this may be simplified to:
//h2[. = 'Foo bar']/following-sibling::p
[1 = count(preceding-sibling::h2[1] | ../h2[. = 'Foo bar'])]
This means: Select all p
elements that are following siblings of the h2
(first or only one in the document) whose string value is 'Foo bar'
and also the first preceding sibling h2
for all these p
elements is exactly the h2(first or only one in the document) whose string value is
'Foo bar'`.
Here we use a method of finding whether two nodes are identical:
count($n1 | $n2) = 1
is true()
exactly when the nodes $n1
and $n2
are the same node.
This expression can be generalized:
$x/following-sibling::p
[1 = count(preceding-sibling::node()[name() = name($x)][1] | $x)]
selects all "immediate following siblings" of any node specified by $x.
XPath : select all following siblings until another sibling
You could do it this way:
../node[not(text()) and preceding-sibling::node[@id][1][@id='1']]
where '1'
is the id of the current node (generate the expression dynamically).
The expression says:
- from the current context go to the parent
- select those child nodes that
- have no text and
- from all "preceding sibling nodes that have an id" the first one must have an id of 1
If you are in XSLT you can select from the following-sibling axis because you can use the current()
function:
<!-- the for-each is merely to switch the current node -->
<xsl:for-each select="node[@id='1']">
<xsl:copy-of select="
following-sibling::node[
not(text()) and
generate-id(preceding-sibling::node[@id][1])
=
generate-id(current())
]
" />
</xsl:for-each>
or simpler (and more efficient) with a key:
<xsl:key
name="kNode"
match="node[not(text())]"
use="generate-id(preceding-sibling::node[@id][1])"
/>
<xsl:copy-of select="key('kNode', generate-id(node[@id='1']))" />
Xpath to find all elements following specific node
*[preceding-sibling::h2]
indicates all elements after h2
. So after adding contains in the selector it goes like this
//div[@id='myDiv']/*[preceding-sibling::h2[contains(text(),'Locator')]]
See Demo Here. Click The Test button you will get the result
How XPath axes preceding:: and following:: work?
This isn't a matter of what you believe, it's a matter of what the specs say. The latest (3.1) version of XPath defines these axes at
https://www.w3.org/TR/xpath-31/#axes
as follows:
the following axis contains all nodes that are descendants of the root
of the tree in which the context node is found, are not descendants of
the context node, and occur after the context node in document orderthe preceding axis contains all nodes that are descendants of the root
of the tree in which the context node is found, are not ancestors of
the context node, and occur before the context node in document order
The definition has not changed over time.
Sadly, it's not unusual for books to simplify. On the other hand, you may have missed something in the books you were reading, you haven't cited them specifically, so I can't tell.
XPath: Select following siblings until certain class
Good question!
The following expression will give you 1..2
, 3..5
or 6..7
, depending on input X + 1
, where X
is the set you want (2 gives 1-2, 3 gives 3-.5 etc). In the example, I select the third set, hence it has [4]
:
/table/tr[1]
/td[not(@class = 'foo')]
[
generate-id(../td[@class='foo'][4])
= generate-id(
preceding-sibling::td[@class='foo'][1]
/following-sibling::td[@class='foo'][1])
]
The beauty of this expression (imnsho) is that you can index by the given set (as opposed to index by relative position) and that is has only one place where you need to update the expression. If you want the sixth set, just type [7]
.
This expression works for any situation where you have siblings where you need the siblings between any two nodes of the same requirement (@class = 'foo'
). I'll update with an explanation.
Replace the [4]
in the expression with whatever set you need, plus 1. In oXygen, the above expression shows me the following selection:
Explanation
/table/tr[1]
Selects the first tr
.
/td[not(@class = 'foo')]
Selects any td
not foo
generate-id(../td[@class='foo'][4])
Gets the identity of the xth foo
, in this case, this selects empty, and returns empty. In all other cases, it will return the identity of the next foo
that we are interested in.
generate-id(
preceding-sibling::td[@class='foo'][1]
/following-sibling::td[@class='foo'][1])
Gets the identity of the first previous foo
(counting backward from any non-foo element) and from there, the first following foo
. In the case of node 7
, this returns the identity of nothingness, resulting in true
for our example case of [4]
. In the case of node 3
, this will result in c
, which is not equal to nothingness, resulting in false
.
If the example would have value [2]
, this last bit would return node b
for nodes 1
and 2
, which is equal to the identity of ../td[@class='foo'][2]
, returning true
. For nodes 4
and 7
etc, this will return false
.
Update, alternative #1
We can replace the generate-id
function with a count-preceding-sibling function. Since the count of the siblings before the two foo
nodes is different for each, this works as an alternative for generate-id
.
By now it starts to grow just as wieldy as GSerg's answer, though:
/table/tr[1]
/td[not(@class = 'foo')]
[
count(../td[@class='foo'][4]/preceding-sibling::*)
= count(
preceding-sibling::td[@class='foo'][1]
/following-sibling::td[@class='foo'][1]/preceding-sibling::*)
]
The same "indexing" method applies. Where I write [4]
above, replace it with the nth + 1 of the intersection position you are interested in.
Select all nodes until a specific given node/tag
Update: much simpler solution
There is a prerequisite in your situation, that is that the anchor item always is the first preceding sibling with a certain property. Because of that, here's a much simpler way of writing the below complex expression:
/div/dl/dd[preceding-sibling::dt[1][. = 'Names']]
In other words:
- select any
dd
- that has a first preceding sibling
dt
(the preceding sibling axis counts backwards) - that itself has a value of "Names"
As can be seen in the following screenshot from oXygen, it selects the nodes you wanted to select (and if you change "Names" to "Status" or "Another Field", it will select only the following ones before the next dt
also).
Original complex solution (leaving in for reference)
This is far easier in XPath 2.0, but let's assume you can only use XPath 1.0. The trick is to count the number of preceding siblings from your anchor element (the one with "Names" in it), and disregard any that have the wrong count (i.e., when we cross over <dt>Status</dt>
, the number of preceding siblings has increased).
For XPath 1.0, remove the comments between (:
and :)
(in XPath, whitespace is insignificant, you can make it a multiline XPath for readability, but in 1.0, comments are not possible)
/div/dl/dd
(: any dd having a dt before it with "Names" :)
[preceding-sibling::dt[. = 'Names']]
(: count the preceding siblings up to dt with "Names", add one to include 'self' :)
[count(preceding-sibling::dt[. = 'Names']/preceding-sibling::dt) + 1
=
(: compare with count of all preceding siblings :)
count(preceding-sibling::dt)]
As a one-liner:
/div/dl/dd[preceding-sibling::dt[. = 'Names']][count(preceding-sibling::dt[. = 'Names']/preceding-sibling::dt) + 1 = count(preceding-sibling::dt)]
Xpath: select preceding and following sibling
This XPath will do that work:
//*[self::p or self::*/li][preceding-sibling::h2 and following-sibling::h2]
Explanations:[preceding-sibling::h2 and following-sibling::h2]
is limiting the sibling elements between the h2
tags.//*[self::p or self::*/li]
selecting p
tag nodes or li
sub-nodes of any kind of parent tag.
It works, you can check it here or with any similar XPath online validator.
XPath - All following siblings except first specific elements
Use:
/table[@id='target']/following-sibling::*[not(self::table) and not(self::ol)]
|
/table[@id='target']/following-sibling::table[position() > 1]
|
/table[@id='target']/following-sibling::ol[position() > 1]
This selects all the following siblings of the table that are not table
and are not ol
and all the following table
siblings with position 2 or greater and all the following ol
siblings with position 2 or greater.
Which is exactly what you want: all following siblings with the exception of the first table
following sibling and the first ol
following siblings.
This is pure XPath 1.0 and not using any XSLT functions.
Related Topics
Devise Custom Routes and Login Pages
Undefined Method Attr_Accessible
Convert Unicode Codepoint to String Character in Ruby
Why Is Sudo: Bundle Command Not Found
Can't Install Ruby Rvm on Ubuntu 16.04 Due to Gpg Bug
How to Set Sslcontext Options in Ruby
Is Order of a Ruby Hash Literal Guaranteed
Best Way to Add Comments in Erb
Bundler Not Working with Rbenv, Could Not Find [Gem]
Uniq by Object Attribute in Ruby
Ruby Local Variable Is Undefined
How to Make an Http Get With Modified Headers
How to Test a Function With Gets.Chomp in It
Execute a Sudo Command in Ruby on Rails App
Cache Resources Exhausted Imagemagick
How to Install Ruby-Debug When Needing Necessary Libraries And/Or Headers