Элементы jQuery не могут выбрать parent()

Кажется, что элементы, выбранные с помощью :contains(sub) с sub, содержащими < или >, не могут добраться до их родителей.

Следующий пример должен проиллюстрировать проблему, с которой я столкнулся как в Safari, так и в Camino (Gecko on Mac):

<html>
 <head>
  <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
 </head>
 <body>
  <p><strong>bar</strong></p>
  <p><strong>&lt;foo&gt;</strong></p>
  <script type="text/javascript">
alert($('body strong:contains("bar")').length);
alert($('body strong:contains("bar")').parent().length);
alert($('body strong:contains("<foo>")').length);
alert($('body strong:contains("<foo>")').parent().length); // this fails
alert($('body strong').length);
alert($('body strong').parent().length); // two p elements
alert($('body strong').parent().parent().length); // one body
  </script>
 </body>
</html>

Выход:

1
1
1
0
2
2
1

Любые идеи, почему четвертый 0 вместо 1, или как я могу обойти это?

Эта страница упоминает экранирование имен в селекторах, но это тоже не сработало (также я не уверен, применимо ли это).

Ответ 1

Не знаю, что привело к ошибке contains(), но вы можете использовать .filter() в качестве альтернативы:

alert($('body strong').filter(function() {
    return /<foo>/.test( $(this).text() );
}).parent().length);

Это возвращает 1, как ожидалось. Это уродливо, но работает.

Ответ 2

Отказ от ответственности: это не решение/обходное решение ответ Tatu для этого, это просто описание проблемы и что происходит, чтобы вызвать странное поведение для любопытных.

Корень проблемы здесь, регулярное выражение, которое jQuery использует для идентификации фрагмента HTML:

/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/

Что соответствует вашему селектору:

body strong:contains("<foo>")

Вы можете попробовать:

alert(/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/.test('body strong:contains("<foo>")​​​'));​

Таким образом, он считает, что это фрагмент HTML. В целом они в настоящее время эквивалентны:

$('body strong:contains("<foo>")');
$('<foo>');

Увидев второе, ясная иллюстрация, что это фрагмент документа... который не имеет родителя. Он занимает 2-ю позицию в массиве совпадений, что вы можете увидеть только <foo>, снова попробовать:

alert(/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/.exec('body strong:contains("<foo>")')[1]);​

Это приведет к тому, что вы в конечном итоге закончите с этот путь кода в jQuery $(), создав фрагмент.

Итак, короче да, я считаю это ошибкой jQuery.

Ответ 3

<foo> оценивается как тег, поэтому родительский элемент не содержит ничего, кроме пустого тега, поэтому 0.

EDIT: Чтобы полностью понять, посмотрите на это: $('body strong:contains("foo")').text().length, который издает 5. 1 из $('body strong:contains("<foo>")').length говорит, что есть один текст node в пределах сильной длины, которая равна 1, а длина текста равна 5.

То, что становится интересным, - это конечная &gt;, которая не может быть выбрана корректно, поскольку кажется, что > и &gt; работают из-за >, который используется как селектор css. Итак, <foo работает, но не <foo>

Вот скрипальная страница: http://jsfiddle.net/2XEUg/1/

Ответ 4

Это явно проблема с синтаксическим селектором jQuery. Если в селекторе присутствуют < и >, jQuery идентифицирует аргумент как фрагмент документа вместо селектора. Результатом является элемент с именем tagName "FOO", и те же селектора будут иметь одинаковую проблему:

$('body <foo>')
$('body strong:not(<foo>')

Единственное различие в вашем случае состоит в том, что вы использовали действительный селектор, а jQuery неправильно идентифицирует его.

Я сделал несколько попыток обходного решения, но Тату Ульманен был единственным, кто работал.

EDIT: кажется, вы также можете использовать .find():

$(document.body).find('strong:contains("<foo>")')