Cri*_*hez 36 javascript css-selectors
有没有办法测试选择器是否匹配给定的DOM元素?优选地,不使用像Sizzle这样的外部库.这是一个库,我想尽量减少"核心"库所需的第三方插件的数量.如果它最终需要Sizzle我只是将它作为插件添加到库中,以便那些想要它将启用的功能.
例如,我可以做类似的事情:
var element = <input name="el" />
matches("input[name=el]", element) == true
Run Code Online (Sandbox Code Playgroud)
编辑:在考虑了更多之后,我提出了一个解决方案,这在技术上有效,但在效率方面似乎不是最佳的:
function matchesSelector(selector, element) {
var nodeList = document.querySelectorAll(selector);
for ( var e in nodeList ) {
return nodeList[e] === element;
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
基本上,函数使用给定的选择器查询整个文档,然后迭代nodeList.如果给定元素在nodeList中,则返回true,如果不是,则返回false.
如果有人能够提出更有效的答案,我很乐意将他们的答案标记为答案.
编辑:Flavius Stef向我指出了Firefox 3.6+,mozMatchesSelector的浏览器特定解决方案.我还发现Chrome的等价物(版本兼容性未知,它可能在Safari或其他webkit浏览器上有效或不起作用):, webkitMatchesSelector
这与Firefox实现基本相同.我还没有找到IE浏览器的任何原生实现.
对于上面的示例,用法将是:
element.(moz|webkit)MatchesSelector("input[name=el]")
Run Code Online (Sandbox Code Playgroud)
似乎W3C也在Selectors API Level 2(此时仍为草案)规范中解决了这个问题.matchesSelector
一旦批准,将成为DOM元素的方法.
W3C用法: element.matchesSelector(selector)
Since that specification is still a draft and there is a lag time before popular browsers implement the methods once it becomes the standard, it may be a while until this actually usable. Good news is, if you use any of the popular frameworks, chances are they probably implement this functionality for you without having to worry about cross browser compatibility. Although that doesn't help those of us who can't include third party libraries.
Frameworks or libraries that implement this functionality:
http://www.prototypejs.org/api/element/match
http://developer.yahoo.com/yui/docs/YAHOO.util.Selector.html
http://docs.jquery.com/Traversing/is
http://extjs.com/deploy/dev/docs/output/Ext.DomQuery.html#Ext.DomQuery-methods
http://base2.googlecode.com/svn/doc/base2.html#/doc/!base2.DOM.Element.matchesSelector
小智 34
为了多年后访问此页面的人的利益,此功能现在在所有现代浏览器中实现,element.matches
没有供应商前缀(除了ms
Edge 15 以外的MS浏览器和webkit
Android/KitKat).请参阅http://caniuse.com/matchesselector.
为获得最佳性能,请(moz|webkit|o|ms)matchesSelector
尽可能使用浏览器实现().当你不能这样做时,这是一个手动实现.
需要考虑的一个重要案例是测试未附加到文档的元素的选择器.
这是一种处理这种情况的方法.如果事实证明element
问题没有附加到文档,爬上树以找到最高的祖先(最后一个非空parentNode
)并将其放入a 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>
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
只要该元素在中,这就可以工作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
Run Code Online (Sandbox Code Playgroud)
但是,如果从元素中删除元素,它将失败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
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
现在我们看到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
Run Code Online (Sandbox Code Playgroud)
你可以在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;
}
Run Code Online (Sandbox Code Playgroud)
一个重要的注意的是,jQuery的是执行速度要快得多.我要研究的第一个优化是,如果我们不必要,就要避免爬树.为此,您可以查看选择器的最右侧部分并测试它是否与元素匹配.但是,请注意,如果选择器实际上是由逗号分隔的多个选择器,那么您将必须测试每个选择器.此时您正在构建一个CSS选择器解析器,因此您也可以使用库.
如果没有xMatchesSelector
,我正在考虑尝试将一个带有请求选择器的样式添加到一个styleSheet
对象,以及一些不太可能已经在使用的任意规则和值.然后检查computed/currentStyle
元素,看它是否继承了添加的CSS规则.对于IE来说这样的东西:
function ieMatchesSelector(selector, element) {
var styleSheet = document.styleSheets[document.styleSheets.length-1];
//arbitrary value, probably should first check
//on the off chance that it is already in use
var expected = 91929;
styleSheet.addRule(selector, 'z-index: '+expected+' !important;', -1);
var result = element.currentStyle.zIndex == expected;
styleSheet.removeRule(styleSheet.rules.length-1);
return result;
}
Run Code Online (Sandbox Code Playgroud)
这个方法可能有一个装满了手提包的手提包.可能最好找到一些不太可能具有视觉效果的晦涩的专有CSS规则z-index
,但由于它在设置后几乎立即被删除,因此短暂的闪烁应该是唯一的副作用.另外一个更加模糊的规则将不太可能被更具体的选择器,样式属性规则或其他重要规则覆盖(如果IE甚至支持这一规则).无论如何,至少值得一试.