javascript 测试选择器是否匹配给定元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3304638/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Test if a selector matches a given element
提问by Cristian Sanchez
Is there any way to test if a selector would match a given DOM Element? Preferably, without the use of an external library like Sizzle. This is for a library and I would like to minimize the amount of third party plugins required for the "core" library. If it ends up requiring Sizzle I'll just add that as a plugin to the library for those who want the feature it would enable.
有什么方法可以测试选择器是否匹配给定的 DOM 元素?最好不要使用像 Sizzle 这样的外部库。这是一个库,我想尽量减少“核心”库所需的第三方插件的数量。如果它最终需要 Sizzle,我会将它作为插件添加到库中,供那些想要它启用的功能的人使用。
For example, I would be able to do something like:
例如,我将能够执行以下操作:
var element = <input name="el" />
matches("input[name=el]", element) == true
EDIT: After thinking about it more, I came up with a solution, this technically works, but it doesn't seem optimal in terms of efficiency:
编辑:在考虑更多之后,我想出了一个解决方案,这在技术上可行,但在效率方面似乎不是最佳的:
function matchesSelector(selector, element) {
var nodeList = document.querySelectorAll(selector);
for ( var e in nodeList ) {
return nodeList[e] === element;
}
return false;
}
Basically the function queries the entire document with the given selector, and then it iterates over the nodeList. If the given element is in the nodeList, then it returns true, and if it isn't it will return false.
基本上,该函数使用给定的选择器查询整个文档,然后遍历 nodeList。如果给定的元素在 nodeList 中,则返回 true,否则返回 false。
If anyone can come up with a more efficient answer I would gladly mark their response as the answer.
如果有人能提出更有效的答案,我很乐意将他们的回复标记为答案。
EDIT: Flavius Stefpointed me towards a browser specific solution for Firefox 3.6+, mozMatchesSelector. I also found the equivalent for Chrome (version compatibility unknown, and it may or may not work on Safari or other webkit browsers): webkitMatchesSelector, which is basically the same as the Firefox implementation. I have not found any native implementation for the IE browsers yet.
编辑:Flavius Stef向我指出了针对 Firefox 3.6+,mozMatchesSelector的浏览器特定解决方案。我还发现了 Chrome 的等效项(版本兼容性未知,它可能在 Safari 或其他 webkit 浏览器上运行,也可能不运行): webkitMatchesSelector,它与 Firefox 实现基本相同。我还没有找到 IE 浏览器的任何本机实现。
For the above example, the usage would be:
对于上面的例子,用法是:
element.(moz|webkit)MatchesSelector("input[name=el]")
It seems the W3C has also addressed this in the Selectors API Level 2 (still a draft at this moment) specification. matchesSelectorwill be a method on DOM Elements once approved.
W3C 似乎也在 Selectors API Level 2(目前仍是草案)规范中解决了这个问题。matchesSelector一旦获得批准,将成为 DOM 元素的方法。
W3C Usage: element.matchesSelector(selector)
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://www.prototypejs.org/api/element/match
http://developer.yahoo.com/yui/docs/YAHOO.util.Selector.html
http://developer.yahoo.com/yui/docs/YAHOO.util.Selector.html
http://docs.jquery.com/Traversing/is
http://docs.jquery.com/Traversing/is
http://extjs.com/deploy/dev/docs/output/Ext.DomQuery.html#Ext.DomQuery-methods
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
http://base2.googlecode.com/svn/doc/base2.html#/doc/!base2.DOM.Element.matchesSelector
回答by Cristian Sanchez
For the benefit of those visiting this page after lo these many years, this functionality is now implemented in all modern browsers as element.matcheswithout vendor prefix (except for msfor MS browsers other than Edge 15, and webkitfor Android/KitKat). See http://caniuse.com/matchesselector.
为了这些年来访问此页面的人的利益,此功能现在在所有现代浏览器中实现,element.matches没有供应商前缀(msEdge 15以外的 MS 浏览器和webkitAndroid/KitKat 除外)。请参阅http://caniuse.com/matchesselector。
回答by Chris Calo
For best performance, use the browser implementations ((moz|webkit|o|ms)matchesSelector) where possible. When you can't do that, here is a manual implementation.
为获得最佳性能,请(moz|webkit|o|ms)matchesSelector尽可能使用浏览器实现 ( )。当你不能这样做时,这里是一个手动实现。
An important case to consider is testing selectors for elements not attached to the document.
要考虑的一个重要情况是测试未附加到文档的元素的选择器。
Here's an approach that handles this situation. If it turns out the the elementin question is not attached to the document, crawl up the tree to find the highest ancestor (the last non-null parentNode) and drop that into a DocumentFragment. Then from that DocumentFragmentcall querySelectorAlland see if the your elementis in the resulting NodeList.
这是处理这种情况的方法。如果事实证明有element问题的 没有附加到文档,则爬上树以找到最高的祖先(最后一个非 null parentNode)并将其放入DocumentFragment. 然后从该DocumentFragment调用中querySelectorAll查看您element的结果是否为NodeList.
Here is the code.
这是代码。
The document
文件
Here's a document structure we'll be working with. We'll grab the .elementand test whether it matches the selectors liand .container *.
这是我们将使用的文档结构。我们将获取.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>
Searching with document.querySelectorAll
搜索 document.querySelectorAll
Here is a matchesSelectorfunction that uses 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;
}
This works as long as that element is in the document.
只要该元素在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
However, it fails if the element is removed from the document.
但是,如果元素从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
Searching within a DocumentFragment
在一个范围内搜索 DocumentFragment
The fix requires searching whatever subtree that elementhappens to be in. Here's an updated function named matchesSelector2.
修复需要搜索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;
}
Now we see that matchesSelector2 works even though the element is in a subtree that is detached from the document.
现在我们看到,即使元素位于与文档分离的子树中,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
You can see this working at jsfiddle.
你可以在jsfiddle看到这个工作。
Putting it all together
把这一切放在一起
Here's the final implementation I came up with:
这是我想出的最终实现:
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;
}
An important note is that jQuery's isimplementation is much faster. The first optimization I would look into is avoiding crawling up the tree if we don't have to. To do this you could look at the right-most part of the selector and test whether this matches the element. However, beware that if the selector is actually multiple selectors separated by commas, then you'll have to test each one. At this point you're building a CSS selector parser, so you might as well use a library.
一个重要的注意的是,jQuery的是执行速度要快得多。我要研究的第一个优化是避免在不需要的情况下爬上树。为此,您可以查看选择器的最右侧部分并测试它是否与元素匹配。但是,请注意,如果选择器实际上是由逗号分隔的多个选择器,那么您必须对每个选择器进行测试。此时您正在构建一个 CSS 选择器解析器,因此您不妨使用一个库。
回答by MooGoo
In the absence of xMatchesSelector, I'm thinking to try adding a style with the requested selector to a styleSheetobject, along with some arbitrary rule and value that is not likely to be already in use. Then check the computed/currentStyleof the element to see if it has inherited the added CSS rule. Something like this for IE:
在没有 的情况下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;
}
There's probably a handbag full of gotcha's with this method. Probably best to find some obscure proprietary CSS rule that is less likely to have a visual effect than z-index, but since it is removed almost immediately after it is set, a brief flicker should be the only side effect if that. Also a more obscure rule will be less likely to be overridden by a more specific selector, style attribute rules, or other !important rules (if IE even supports that). Anyway, worth a try at least.
这种方法可能有一个装满陷阱的手提包。可能最好找到一些不太可能产生视觉效果的晦涩的专有 CSS 规则z-index,但由于它在设置后几乎立即被删除,如果那样的话,短暂的闪烁应该是唯一的副作用。此外,更模糊的规则不太可能被更具体的选择器、样式属性规则或其他 !important 规则覆盖(如果 IE 甚至支持)。无论如何,至少值得一试。
回答by Flavius Stef
The W3C selectors API (http://www.w3.org/TR/selectors-api/) specifies document.querySelectorAll(). This is not supported on all browsers, so you'd have to google the ones that do support it: http://www.google.com/search?q=browsers+implementing+selector+api
W3C 选择器 API ( http://www.w3.org/TR/selectors-api/) 指定document.querySelectorAll(). 并非所有浏览器都支持此功能,因此您必须搜索支持它的浏览器:http: //www.google.com/search?q=browsers+implementing+selector+ api
回答by zslayton
I'm dealing with this issue now. I have to support IE8 with native Javascript, which presents a curious challenge: IE8 supports both querySelector and querySelectorAll, but not matchesSelector. If your situation is similar, here's an option for you to consider:
我现在正在处理这个问题。我必须用原生 Javascript 支持 IE8,这提出了一个奇怪的挑战:IE8 支持 querySelector 和 querySelectorAll,但不支持matchesSelector。如果您的情况类似,这里有一个选项供您考虑:
When you're handed the DOM node and a selector, make a shallow copy of the node as well as its parent. This will preserve all of their attributes but won't make copies of their respective children.
当您收到 DOM 节点和选择器时,制作该节点及其父节点的浅表副本。这将保留他们的所有属性,但不会复制他们各自的孩子。
Attach the cloned node to the cloned parent. Use querySelector on the cloned parent -- the only thing it needs to search is the only child node it has so this process is constant time. It will either return the child node or it won't.
将克隆的节点附加到克隆的父节点。在克隆的父节点上使用 querySelector —— 它唯一需要搜索的是它拥有的唯一子节点,因此这个过程是恒定时间。它要么返回子节点,要么不返回。
That'd look something like this:
看起来像这样:
function matchesSelector(node, selector)
{
var dummyNode = node.cloneNode(false);
var dummyParent = node.parent.cloneNode(false);
dummyParent.appendChild(dummyNode);
return dummyNode === dummyParent.querySelector(selector);
}
It may be worth creating a complete chain of shallow-copied parents all the way up to the root node and querying the (mostly empty) dummy root if you'd like to be able to test your node's relationship to its ancestors.
如果您希望能够测试您的节点与其祖先的关系,则可能值得创建一个完整的浅复制父级一直到根节点并查询(大部分为空)虚拟根。
Off the top of my head I'm not sure what portion of selectors this would work for, but I think it'd do nicely for any that didn't worry about the tested node's children. YMMV.
在我的头顶上,我不确定这对选择器的哪个部分有用,但我认为它对任何不担心测试节点的子节点的人都很好。天啊。
-- EDIT --
- 编辑 -
I decided to write the function to shallow copy everything from the node being tested to root. Using this, a lot more selectors are employable. (Nothing related to siblings, though.)
我决定编写函数来浅复制从被测试节点到根节点的所有内容。使用它,可以使用更多的选择器。(不过,与兄弟姐妹无关。)
function clonedToRoot(node)
{
dummyNode = node.cloneNode(false);
if(node.parentNode === document)
{
return {'root' : dummyNode, 'leaf' : dummyNode};
}
parent = clonedToRoot(node.parentNode).root;
parent.appendChild(dummyNode);
return {'root' : parent, 'leaf' : dummyNode};
}
function matchesSelector(node, selector)
{
testTree = clonedToRoot(node)
return testTree.leaf === testTree.root.querySelector(selector)
}
I'd welcome an expert to explain what kinds of selectors there are that this wouldn't cover!
我欢迎专家解释哪些类型的选择器,这不会涵盖!
回答by Dagg Nabbit
Modern browsers can do it with the document.querySelectorAllfunction.
现代浏览器可以使用该document.querySelectorAll功能来完成。
回答by sebastian
Just use an id for your element? HTML-IDs have to be unique…
只为你的元素使用一个 id?HTML-ID 必须是唯一的……

