Подтвердить что ты не робот

Есть ли лучший способ получить родительский 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> вместо своего контейнера.

4b9b3361

Ответ 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 ')]