user3674248
user3674248

Reputation: 241

XPath: how to select following siblings until a certain sibling

For the example HTML below, an XPath query that returns the siblings of the "a" elements with class='A' that have class='B' can be written as: //a[@class='A']/following-sibling::a[@class='B']. This query outputs 4 <a class="B"/> elements.

However, I would only like the <a class="B"/> elements that follow the current <a class="A"/> element, and no others that follow other <a class="B"/> elements/nodes. In other words, I only want the following <a class="B"/> sibling elements until the next <a class="B"/> element shows up.

Example HTML:

<a class='A' />
<a class='B' />
<a class='A' />
<a class='B' />
<a class='B' />
<a class='B' />

Any ideas on how to limit my current XPath query to just those siblings would be much appreciated :)

Upvotes: 24

Views: 17306

Answers (3)

Joel M. Lamsen
Joel M. Lamsen

Reputation: 7173

try it this way:

//a[@class='B'][preceding-sibling::*[1][name()='a'][@class='A']]

Upvotes: -1

Wayne
Wayne

Reputation: 60414

To select all a elements having a class attribute of B between some specific a with a class equal to A and the next such occurrence:

/*/a[@class='A'][$n]/following-sibling::a[
    @class='B' and count(preceding-sibling::a[@class='A'])=$n]

This selects everything between the nth a[@class='A'] and the next such element. For a specific example, consider the following input:

<r>
  <a class="A"/>  
  <a class="B"/>  
  <a class="A"/>  
  <a class="B"/>  
  <a class="B"/>  
  <a class="A"/>  
  <a class="B"/>  
  <a class="B"/>  
  <a class="B"/>
</r>

To get the two elements between the second <a class="A"/> and the third <a class="A"/>:

/*/a[@class='A'][2]/following-sibling::a[
    @class='B' and count(preceding-sibling::a[@class='A'])=2]

In English, this says:

Give me all of the a elements having a class attribute whose value is equal to B that come after the second a having a class attribute equal to A and that have only two preceding siblings having a class attribute equal to A

Similarly, and more generally, we can apply the Kayessian method for finding the intersection of two node sets. In the example given, we want the intersection of all the @class='B' elements in 1) the set of siblings after the second <a class="A"/> and 2) the set of siblings before the third <a class="A"/>. The intersection of these two sets is precisely the nodes that come between those two divider elements and can be expressed like this:

/*/a[@class='A'][2]/following-sibling::a[@class='B'][
    count(.|/*/a[@class='A'][3]/preceding-sibling::a[@class='B'])=
    count(/*/a[@class='A'][3]/preceding-sibling::a[@class='B'])]

Upvotes: 16

har07
har07

Reputation: 89285

You can try this way :

//a[
    @class='B' 
        and 
    preceding-sibling::a[@class='A'] 
        and 
    following-sibling::a[@class='A']
   ]

Above XPath will select all <a class='B'> element(s) between the two <a class='A'> elements.

Upvotes: 4

Related Questions