Есть ли лучший способ получить родительский node результат запроса XPath?
Данная разметка выглядит следующим образом:
<div class="foo">
<div><span class="a1"></span><a href="...">...</a></div>
<div><span class="a2"></span><a href="...">...</a></div>
<div><span class="a1"></span>some text</div>
<div><span class="a3"></span>some text</div>
</div>
Теперь мне интересно получить эти <a>
и some text
ТОЛЬКО, если соседний span
имеет класс a1
. Поэтому в конце всего кода мой результат <a>
из первых div
и some text
из третьего. Было бы легко, если бы <a>
и some text
находились внутри span
или div
имели бы атрибут class
, но не повезло.
Теперь я делаю запрос для span
с классом a1
следующим образом:
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]
а затем получить его родительский элемент и сделать еще один query()
с этим родителем node в качестве контекста node. Это выглядит далеко не эффективным, так есть ли лучший способ сделать это?
ОТВЕТ
В соответствии с @MarcB answer правильный запрос:
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/..
но для <a>
может быть лучше использовать:
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/../a
получить <a>
вместо своего контейнера.
Ответы
Ответ 1
Хорошая вещь о запросах xpath заключается в том, что вы можете рассматривать их как путь к файловой системе, поэтому просто
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/..
^^
найдет все ваши узлы .a1, которые находятся ниже .foo node, а затем переместитесь на один уровень к родителям a1 узлов.
Ответ 2
Выражение, которое лучше, чем использование обратной оси:
//div[contains(@class,'foo')]/div[span[contains(@class,'a1')]]
Это выбирает любой div
, являющийся дочерним элементом div
, атрибут class
содержит строку "foo" и что (выбранный div
) имеет дочерний элемент span
, чей атрибут class
содержит строка "a1".
Проверка на основе XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"//div[contains(@class,'foo')]
/div[span[contains(@class,'a1')]]"/>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к предоставленному XML-документу:
<div class="foo">
<div><span class="a1"></span><a href="...">...</a></div>
<div><span class="a2"></span><a href="...">...</a></div>
<div><span class="a1"></span>some text</div>
<div><span class="a3"></span>some text</div>
</div>
выражение XPath оценивается и выбранные элементы копируются на вывод:
<div>
<span class="a1"/>
<a href="...">...</a>
</div>
<div>
<span class="a1"/>some text</div>
II. Замечания о доступе к элементу Html одним из его классов:
Если известно, что элемент может иметь только один класс, то вообще не нужно использовать contains()
Не используйте:
//div[contains(@class, 'foo')]
Использование
//div[@class = 'foo']
или, если могут быть ведущие/конечные пробелы, используйте:
//div[normalize-space(@class) = 'foo']
Важная проблема с:
//div[contains(@class, 'foo')]
заключается в том, что он выбирает любой div
с классом, таким как "myfoo", "foo2" или "myfoo3".
Если элемент может иметь более одного класса и, чтобы избежать вышеупомянутой проблемы, правильное выражение XPath:
//div[contains(concat(' ', @class, ' '), ' foo ')]