querySelector,获取属性以给定字符串开头的元素

时间:2016-07-30 10:21:22

标签: javascript html5 dom selectors-api

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

xhtml:link[href][rel=alternate]会返回包含Angular指令的所有元素(document.querySelectorAll('[ng-*]')ng-clickng-show ...)。

但是通配符似乎不起作用。

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

2 个答案:

答案 0 :(得分: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>
&#13;
&#13;
&#13;

ES5版本:

&#13;
&#13;
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);
&#13;
<div></div>
<div ng-bar></div>
<div></div>
<div ng-foo></div>
<div></div>
&#13;
&#13;
&#13;

沃克解决方案(ES2015 +):

&#13;
&#13;
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);
&#13;
<div></div>
<div ng-bar></div>
<div></div>
<div>
  <div>
    <div ng-foo></div>
  </div>
  <div ng-baz></div>
</div>
<div></div>
&#13;
&#13;
&#13;

ES5:

&#13;
&#13;
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);
&#13;
<div></div>
<div ng-bar></div>
<div></div>
<div>
  <div>
    <div ng-foo></div>
  </div>
  <div ng-baz></div>
</div>
<div></div>
&#13;
&#13;
&#13;

对于上述所有情况,请注意{ES} 2015之前String#startsWith可能需要填充垫片。

答案 1 :(得分:1)

@ T.J.Crowder的回答是对的。

但IMK实现自定义querySelector方法的更快捷方式绝对应该使用TreeWalker

使用T.J提出的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:这个答案的任何upvote也应该奖励给T.J的。