#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
Комментариев нет:
Отправить комментарий