2016-07-30 221 views
0

我正在寻找一种方法来查找包含以给定字符串开头的属性的所有元素。例如:querySelector,获取其属性以给定字符串开头的元素

document.querySelectorAll('[ng-*]')将使用Angular指令返回所有元素(ng-click,ng-show,ng-hide ...)。

但通配符似乎不起作用。

任何想法?我可以实现我自己的,但它不会那么有效。

+1

有没有办法做到这一点。 – 2016-07-30 10:24:17

+0

请参阅http://stackoverflow.com/questions/13222334/wildcards-in-html5-data-attributes或http://stackoverflow.com/questions/27326470/is-it-possible-to-wildcard-for-an- attribute-name-with-a-wildcarded-attribute或http://stackoverflow.com/questions/21222375/css-selector-for-attribute-names-based-on-a-wildcard/36891269#36891269。 – 2016-07-30 10:25:38

+0

@torazaburo:这些都不适用,因为它们都是jQuery特有的。 –

回答

4

有没有CSS选择器,可以让你做名称通配符的属性,只是在值,这显然不是你想要的。

不幸的是,您必须手动完成这项工作。

如果你知道你将要处理的列表很小,你可以使用一个天真的版本查询然后过滤器;如果你认为它可能更大,那么DOM walker解决方案(见下文)可能会更好。

天真查询然后过滤器(ES2015语法):

// Best if you can use *some* selector in the querySelectorAll rather than just * 
 
// to keep the list to a minimum 
 
let elements = 
 
    [...document.querySelectorAll("*")].filter(
 
    element => [...element.attributes].some(
 
     attr => attr.nodeName.startsWith("ng-") 
 
    ) 
 
); 
 
console.log(elements);
<div></div> 
 
<div ng-bar></div> 
 
<div></div> 
 
<div ng-foo></div> 
 
<div></div>

ES5版本:

var elements = Array.prototype.filter.call(
 
    document.querySelectorAll("*"), // Best if you can use *something* here 
 
            // to keep the list to a minimum 
 
    function(element) { 
 
    return Array.prototype.some.call(
 
     element.attributes, 
 
     function(attr) { 
 
     return attr.nodeName.startsWith("ng-"); 
 
     } 
 
    ); 
 
    } 
 
); 
 
console.log(elements);
<div></div> 
 
<div ng-bar></div> 
 
<div></div> 
 
<div ng-foo></div> 
 
<div></div>


沃克溶液(ES2015 +):

function domFind(element, predicate, results = []) { 
 
    if (!element.children) { 
 
    throw new Error("Starting node must be an element or document"); 
 
    } 
 
    if (predicate(element)) { 
 
    results.push(element); 
 
    } 
 
    if (element.children && element.children.length) { 
 
    [...element.children].forEach(child => { 
 
     domFind(child, predicate, results); 
 
    }); 
 
    } 
 
    return results; 
 
} 
 
let elements = domFind(document, element => { 
 
    return element.attributes && [...element.attributes].some(attr => attr.nodeName.startsWith("ng-")); 
 
}); 
 
console.log(elements);
<div></div> 
 
<div ng-bar></div> 
 
<div></div> 
 
<div> 
 
    <div> 
 
    <div ng-foo></div> 
 
    </div> 
 
    <div ng-baz></div> 
 
</div> 
 
<div></div>

ES5:

function domFind(element, predicate, results) { 
 
    if (!results) { 
 
    results = []; 
 
    } 
 
    if (!element.children) { 
 
    throw new Error("Starting node must be an element or document"); 
 
    } 
 
    if (predicate(element)) { 
 
    results.push(element); 
 
    } 
 
    if (element.children && element.children.length) { 
 
    Array.prototype.forEach.call(element.children, function(child) { 
 
     domFind(child, predicate, results); 
 
    }); 
 
    } 
 
    return results; 
 
} 
 
var elements = domFind(document, function(element) { 
 
    return element.attributes && Array.prototype.some.call(element.attributes, function(attr) { 
 
    return attr.nodeName.startsWith("ng-"); 
 
    }); 
 
}); 
 
console.log(elements);
<div></div> 
 
<div ng-bar></div> 
 
<div></div> 
 
<div> 
 
    <div> 
 
    <div ng-foo></div> 
 
    </div> 
 
    <div ng-baz></div> 
 
</div> 
 
<div></div>


对于所有的请注意,在ES2015之前,String#startsWith可能需要垫片。

+0

这是我认为的一个重复。 – 2016-07-30 10:26:03

+1

是的,我猜我必须手动完成。会做一个小功能一个要点。谢谢! –

+1

谢谢TJ。我添加了正则表达式支持。 https://gist.github.com/jsmrcaga/b3c14be5138256dca1d94993d5e6d7b4 –

1

@ T.J.Crowder的答案是正确的。

但是,IMK更快地实施自定义querySelector方法的方法肯定应该使用TreeWalker来完成。

这是一个简单的(可能不是完美的)实现你使用T提出的ng-过滤。记者:

var filterAttr = function(element) { 
 
    return Array.prototype.some.call(
 
    element.attributes, 
 
    function(attr) { 
 
     return attr.nodeName.indexOf("ng-") === 0; 
 
    } 
 
    // webkit defaults to FILTER_REJECT 
 
) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 
 
}; 
 
// IE doesn't like {acceptNode:...} object 
 
filterAttr.acceptNode = filterAttr; 
 

 
var treeWalker = document.createTreeWalker(
 
    document.body, 
 
    NodeFilter.SHOW_ELEMENT, 
 
    filterAttr, 
 
    false 
 
); 
 
var nodeList = []; 
 
while (treeWalker.nextNode()) { 
 
    nodeList.push(treeWalker.currentNode); 
 
} 
 

 
console.log(nodeList);
<div></div> 
 
<div ng-bar></div> 
 
<div></div> 
 
<div> 
 
    <div> 
 
    <div ng-foo></div> 
 
    </div> 
 
    <div ng-baz></div> 
 
</div> 
 
<div></div>

PS:这个回答任何给予好评也应奖励给T.J的一个了。

+0

奇怪的是,当我运行它时,我只能看到'ng-bar'节点,而不是'ng-foo'或'ng-baz'。 –

+0

@ Chrome上的T.J.Crowder?只在FF上显示“foo”和“baz”。确实很奇怪。让我在调查时删除它。好的,可以重播,我只是不明白这是从哪里来的......我现在会保持删除状态,并且可能会在明天发布一个关于它的问题,甚至会向铬报告错误报告? – Kaiido

+0

@ T.J.Crowder,我终于明白了,当自定义过滤函数返回false时,webkit浏览器将简单地默认为'NodeFilter.FILTER_REJECT',而FF返回'FILTER_SKIP'。注意确定谁是对的,但无论如何都要写清楚;-)另外IE不喜欢'{acceptNode:function(node){..}}'版本,似乎在等待带有'filterNode的函数财产。疯狂多少我可以用一个单一的答案学习,我试图写。 – Kaiido