XPath: how to select following siblings until a certain sibling

html, xpath

Solution

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'])]

Problem

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 :)

Original source

Related problems