Xeoncross
Xeoncross

Reputation: 57184

Find the first ancestor node with X attribute using XPath

I have duplicate nested structures in my DOM and I am trying to find certain input elements at the current node level.

For example, say the current node was <div data-model="user" data-id="1">. How could I get the the <input value=foo without getting the <input value=bar (since it's under it's own data-model)?

<div>
    <div data-model="user" data-id="1">
        <div>
            <input data-field="email" value="foo">

            <div data-model="user" data-id="2">
                <div>
                    <input data-field="email" value="bar">
                </div>
            </div>
        </div>
    </div>
</div>

Here is what I have in an xpath so far:

//*[@data-model="user"]//*[@data-field and ancestor::*[@data-model="user" ....]]

Upvotes: 1

Views: 305

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122364

You could do it in two steps. First evaluate

count(ancestor-or-self::div[@data-model = 'user'])

as a number, with the current div as the context node. Then take that number $n and evaluate

.//input[count(ancestor::div[@data-model = 'user']) = $n]

The idea here is to find all descendant input elements that have the same number of containing data-model="user" divs as the context node we started from (inclusive).

From XSLT you could do this in a single pass because you have access to the current() function which lets you "escape" from the predicate

.//input[count(ancestor::div[@data-model = 'user']) = 
  count(current()/ancestor-or-self::div[@data-model = 'user'])]

This function isn't available in pure XPath but if your library lets you pass variable bindings to your expressions then you could provide the context node in a variable and use that in place of current().

Upvotes: 1

Related Questions