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

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

Следующий пример должен проиллюстрировать проблему, с которой я столкнулся как в Safari, так и в Camino (Gecko на 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или как мне это обойти?

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

4 ответа

Решение

Понятия не имею, что вызывает contains() потерпеть неудачу, но вы можете использовать .filter() как альтернатива:

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

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

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

Корень проблемы здесь, регулярное выражение, которое 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.

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

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

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

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

РЕДАКТИРОВАТЬ: кажется, вы также можете использовать .find ():

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

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

РЕДАКТИРОВАТЬ: Чтобы полностью понять, посмотрите на это: $('body strong:contains("foo")').text().length который дает 5. 1 из $('body strong:contains("<foo>")').length говорит, что внутри сильного есть один текстовый узел, длина которого равна 1, а длина текста равна 5.

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

Вот страница скрипки, чтобы поиграть с ней: http://jsfiddle.net/2XEUg/1/

Другие вопросы по тегам