为获得最佳性能,请(moz|webkit|o|ms)matchesSelector尽可能使用浏览器实现 ( )。当你不能这样做时,这里是一个手动实现。
要考虑的一个重要情况是测试未附加到文档的元素的选择器。
这是处理这种情况的方法。如果事实证明有element问题的 没有附加到文档,则爬上树以找到最高的祖先(最后一个非 null parentNode)并将其放入DocumentFragment. 然后从该DocumentFragment调用中querySelectorAll查看您element的结果是否为NodeList.
这是代码。
文件
这是我们将使用的文档结构。我们将获取.element并测试它是否与选择器li和匹配.container *。
<!DOCTYPE html>
<html>
<body>
<article class="container">
<section>
<h1>Header 1</h1>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</section>
<section>
<h1>Header 2</h1>
<ul>
<li>one</li>
<li>two</li>
<li class="element">three</li>
</ul>
</section>
<footer>Footer</footer>
</article>
</body>
</html>
搜索 document.querySelectorAll
这是一个matchesSelector使用document.querySelectorAll.
// uses document.querySelectorAll
function matchesSelector(selector, element) {
var all = document.querySelectorAll(selector);
for (var i = 0; i < all.length; i++) {
if (all[i] === element) {
return true;
}
}
return false;
}
只要该元素在document.
// this works because the element is in the document
console.log("Part 1");
var element = document.querySelector(".element");
console.log(matchesSelector("li", element)); // true
console.log(matchesSelector(".container *", element)); // true
但是,如果元素从document.
// but they don't work if we remove the article from the document
console.log("Part 2");
var article = document.querySelector("article");
article.parentNode.removeChild(article);
console.log(matchesSelector("li", element)); // false
console.log(matchesSelector(".container *", element)); // false
在一个范围内搜索 DocumentFragment
修复需要搜索element碰巧在其中的任何子树。这是一个名为 的更新函数matchesSelector2。
// uses a DocumentFragment if element is not attached to the document
function matchesSelector2(selector, element) {
if (document.contains(element)) {
return matchesSelector(selector, element);
}
var node = element;
var root = document.createDocumentFragment();
while (node.parentNode) {
node = node.parentNode;
}
root.appendChild(node);
var all = root.querySelectorAll(selector);
for (var i = 0; i < all.length; i++) {
if (all[i] === element) {
root.removeChild(node);
return true;
}
}
root.removeChild(node);
return false;
}
现在我们看到,即使元素位于与文档分离的子树中,matchesSelector2 也能工作。
// but they will work if we use matchesSelector2
console.log("Part 3");
console.log(matchesSelector2("li", element)); // true
console.log(matchesSelector2(".container *", element)); // true
你可以在jsfiddle看到这个工作。
把这一切放在一起
这是我想出的最终实现:
function is(element, selector) {
var node = element;
var result = false;
var root, frag;
// crawl up the tree
while (node.parentNode) {
node = node.parentNode;
}
// root must be either a Document or a DocumentFragment
if (node instanceof Document || node instanceof DocumentFragment) {
root = node;
} else {
root = frag = document.createDocumentFragment();
frag.appendChild(node);
}
// see if selector matches
var matches = root.querySelectorAll(selector);
for (var i = 0; i < matches.length; i++) {
if (this === matches.item(i)) {
result = true;
break;
}
}
// detach from DocumentFragment and return result
while (frag && frag.firstChild) {
frag.removeChild(frag.firstChild);
}
return result;
}
一个重要的注意的是,jQuery的是执行速度要快得多。我要研究的第一个优化是避免在不需要的情况下爬上树。为此,您可以查看选择器的最右侧部分并测试它是否与元素匹配。但是,请注意,如果选择器实际上是由逗号分隔的多个选择器,那么您必须对每个选择器进行测试。此时您正在构建一个 CSS 选择器解析器,因此您不妨使用一个库。