2016-07-30 220 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