如何在纯JavaScript中像jQuery一样过滤元素?

7
例如,在jQuery中,如果我想选择所有的<div><p>元素,我可以这样做:
var $x = $("p, div");

如果我想获取 x 中所有 <div> 元素,那么我可以这样做:

var $divs = $x.filter("div");

那么如何在原生JavaScript中实现这个简单的filter呢?

例如,要选择所有的<div><p>,可以这样做:

var x = document.querySelectorAll("div, p");

但是纯JavaScript没有像jQuery中的filter函数,所以我无法做到这一点:
var divs = x.filter("div"); // ERROR

希望有人能帮我解决这个问题:-)
提前感谢。
更新
一些评论/答案建议做类似于.tagName == "DIV"的事情来查找div,但是,我想要一个像jQuery中的字符串选择器一样的解决方案。
原因是我还想通过属性、类甚至多个用逗号隔开的选择器进行过滤。并且字符串选择器必须是动态的。
也就是说,我不知道选择器中有什么。它可能是"div[foo='asd'], .bar"或者"#hello, [xx='yy'], p"。
因此,我不能硬编码.tagName == "DIV",因为我不知道选择器字符串中有什么。

我不确定,但我认为您可以只需执行[anyDomElement].GetElementsByTagName("div") - Marie
2
你是否打开过jQuery核心代码并查看了.filter()方法? - Roko C. Buljan
1
没有所谓的“本地”JavaScript……它通常被称为“原生”或ECMA6脚本。 - Martin Zeitler
@Roko C. Buljan请查看以下网站:http://es6-features.org - Martin Zeitler
1
@MartinZeitler所指的是ECMAScript吧 ;) - Roko C. Buljan
显示剩余4条评论
5个回答

5
您可以使用 matches 函数来检查 CSS 选择器是否与给定元素匹配。
var x = document.querySelectorAll("div, p");
var divs = [];

// Iterate through the elements that the first query selector matched
//  (is a paragraph or a div tag)
for (var elem of x) {
    // Check if the given element matches the second query selector
    //  (is a div tag)
    if (elem.matches('div')) {
        divs.push(elem);
    }
}

这段代码可以用更简洁的方式来编写(而且使用更现代的代码):

let x = document.querySelectorAll("div, p");
let divs = Array.from(x).filter(elem => elem.matches("div"));

1
你可以将 Array.filter()Element.matches() 结合使用。
var x = document.querySelectorAll("div, p");
var divs = x.filter(y => y.matches("div"));

// for p tags
var paragraphs = x.filter(y => y.matches("p"));

//for both div and p tags
var mix = x.filter(y => y.matches("div, p"));

2
Element.matches() works really well. The only thing wrong with this answer is that .filter() isn't a method of a NodeList, so you have to convert the NodeList to an Array first. Example: [...x].filter() - burkybang

1

你可以使用Array.filter

const elems = document.querySelectorAll('p, div');
const divs = [...elems].filter(e => {
  return e.tagName == 'DIV'
});

console.log(divs)
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
<p id="p1"></p>
<p id="p2"></p>
<p id="p3"></p>

你可以将e.tagName更改为其他过滤条件:

const elems = document.querySelectorAll('p, div');

const divs = [...elems].filter(e => {
  return e.tagName == 'DIV'
});

const byId = [...elems].filter(e => {
  return e.id == 'div1'
});

const byClass = [...elems].filter(e => {
  return e.className == 'class1'
});

console.log(byClass)
<div id="div1" class="class1"></div>
<div id="div2" class="class1"></div>
<div id="div3" class="class2"></div>
<p id="p1" class="class1"></p>
<p id="p2" class="class2"></p>
<p id="p3" class="class2"></p>


你如何通过ID或class进行筛选?或者使用属性选择器[] - Roko C. Buljan
只需将 e.tagName 替换为 e.classNamee.id,例如: const byClass = [...elems].filter(e => {return e.className == 'class1'}); - Taki
@RokoC.Buljan,不建议使用硬编码和替换,但这只是一个想法,理想情况下,应该调整并包装成带有参数的函数以便更好地使用。 - Taki

0

querySelectorAll 返回的是 NodeList 而不是数组。

你需要将其转换为数组。

var arr = Array.prototype.slice.call(x);
var divs = arr.filter(el => { return el.tagName == 'DIV' });

1
这如何回答“如何复制jQuery的.filter()”问题? - Roko C. Buljan
抱歉,我之前假设你已经了解相关知识。我更新了我的答案以使其更完整。我想将NodeList转换为Array可以让用户理解.filter()现在可用了。总之,现在有了.filter()函数,你可以按标签名进行排序了。 - CAOakley
可以使用 Array.prototype 代替 [] 来避免不必要的数组实例化。 - Taplar
1
const arr = [...document.querySelectorAll("selector")].filter( 等等,这是关于编程的内容。 - Roko C. Buljan
另外,如果你喜欢缩写,y 是相当奇怪的。另一方面,el 则是众所周知的... - Roko C. Buljan

0
一个非常天真的方法:
function get(selector) {
 const els = document.querySelectorAll(selector);

 return {
   selector,
   length: els.length,
   get: i => els[i],
   [Symbol.iterator]: () => els[Symbol.iterator](),
   filter: f => get(selector + ", " + f)
  };
}

可用于:

const menus = get(".menu");
for(const el of menus) console.log(el);
console.log(menus.filter("div").get(0));

抱歉,menus.filter("div").get(0) 应该返回什么? - Roko C. Buljan
@roko 第一个是 div.menu - Jonas Wilms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接