#javascript #jquery
Как известно, jQuery.fn.find по возможности пытается скормить селектор браузерному
querySelectorAll.
При этом есть вариант, когда для некоторого элемента генерируется id, после чего
браузеру достаётся селектор, в начало которого добавилась преверка на этот id.
Зачем вообще нужен этот фокус разобрался. Селектор ставится в соответствие не внутри
элемента, а вообще в документе. Просто фильтруется поддерево.
Вопрос зачем делается выборка по атрибуту вместо id.
Т. е. почему nid = "[id='" + nid + "'] ";, а не nid = "#" + nid + " ";?
Более полный контекст для двух версий приведён далее:
1.8.1
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
var groups, i, len,
old = context.getAttribute("id"),
nid = old || expando,
newContext = rsibling.test( selector ) && context.parentNode || context;
if ( old ) {
nid = nid.replace( rescape, "\\$&" );
} else {
context.setAttribute( "id", nid );
}
groups = tokenize(selector, context, xml);
// Trailing space is unnecessary
// There is always a context check
nid = "[id='" + nid + "']";
for ( i = 0, len = groups.length; i < len; i++ ) {
groups[i] = nid + groups[i].selector;
}
try {
push.apply( results, slice.call( newContext.querySelectorAll(
groups.join(",")
), 0 ) );
return results;
} catch(qsaError) {
} finally {
if ( !old ) {
context.removeAttribute("id");
}
}
}
1.11.3
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
groups = tokenize( selector );
if ( (old = context.getAttribute("id")) ) {
nid = old.replace( rescape, "\\$&" );
} else {
context.setAttribute( "id", nid );
}
nid = "[id='" + nid + "'] ";
i = groups.length;
while ( i-- ) {
groups[i] = nid + toSelector( groups[i] );
}
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
context;
newSelector = groups.join(",");
}
Ответы
Ответ 1
Коротко о проблеме:JS: var foo = document.getElementById("foo"); console.log( foo.querySelectorAll('div span').length ); // 1 Проблема querySelectorAll в том, что сначала он вытащит все что подходит под селектор div span начиная с корня документа, а потом отфильтрует полученный результат на вхождение в элемент-контекст (foo) - т.е. селектор div span найдет один span и да - этот span находится внутри #foo. Трюк с аттрибутами используется как раз для того, чтобы исключить элемент контекста и начинать запрос сразу с корня документа, избегая такой странной выборки. Небольшое пояснение кода: if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } Если у элемента-контекста есть id, то используем его, а иначе добавляем ему id с уникальным только что созданным хешем. nid = "[id='" + nid + "'] "; Преобразуем в селектор аттрибута, благодаря операции выше мы уверены, что у элемента-контекста всегда будет id - либо уникальный либо сгенерированный. i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } Группы - это если селекторов несколько (перечислены через запятую). Здесь в начало каждой группы добавляется селектор аттрибута. Если брать оригинальный пример с запросом foo.querySelectorAll('div span'), то группа в данном случае это div span, а после добавления аттрибута получится [id="foo"] div span. Пре передаче этого нового селектора в querySelectorAll запрос будет выполнен уже в рамках элемента-контекста не смотря ни на что. Используются аттрибуты [id="foo"] вместо #foo, т.к. идентификатор по спецификации может сожержать спецсимволы, которые очень сложно эскейпить самостоятельно, и в команде было принято решение скинуть парсинг идентификатора на браузер. Источники: John Resig - Thoughts on querySelectorAll jquery/sizzle - Replace ID-attribute selectors with ID-selectors where possible #354 jquery/sizzle - Bug #7533. Fix for Element-rooted queries where ID contains CSS3 selector meta characters jquery/sizzle - Bug #7533. Fix for IDs which contain CSS3 selector meta characters #37
Комментариев нет:
Отправить комментарий