7

For example, in jQuery, if i want all <div> and <p> elements, I do this:

var $x = $("p, div");

And then if i want all the <div> elements in x, then I do this:

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

So how do i do this simple filter thing in vanilla JavaScript?

For example, to select all <div> and <p>, then I can do this:

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

But vanilla JavaScript doesn't have the filter function like in jQuery, so I can't do this:

var divs = x.filter("div"); // ERROR

Hope someone can help me with this :-)

Thanks in advance.

UPDATE

Some comments/answers have suggested to do something like .tagName == "DIV" to find the divs, however, i want a solution with a string selector like in jQuery.

The reason is because i also want to filter with attributes, classes and even multiple selectors where you put in comma. And the string selector must be dynamic.

That means, i dont know whats in the selector. It could be "div[foo='asd'], .bar" or "#hello, [xx='yy'], p"

So i cant just hardcode the .tagName == "DIV", beacuse i dont know whats in the selector string.

Assassinbeast
  • 1,207
  • 1
  • 17
  • 33

5 Answers5

5

You can use the matches function to check if a CSS selector matches a given element.

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);
    }
}

This code can be written more succinctly (and with more modern code) using:

let x = document.querySelectorAll("div, p");
let divs = Array.from(x).filter(elem => elem.matches("div"));
Dave F
  • 1,837
  • 15
  • 20
1

you can use 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>

you can change e.tagName to filter with something else :

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>
Taki
  • 17,320
  • 4
  • 26
  • 47
  • how do you filter by ID or class? Or by Attribute selector `[]`? – Roko C. Buljan May 29 '18 at 20:22
  • just replace `e.tagName` with `e.className` or `e.id` , like : `const byClass = [...elems].filter(e => {return e.className == 'class1'});` – Taki May 29 '18 at 20:25
  • @RokoC.Buljan , def. hardcoding and replacing is not advisable, but this just gives an idea, ideally, this would be tweaked and wrapped in a function with arguments for better use. – Taki May 29 '18 at 20:33
1

you can combine Array.filter() with 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 Jul 10 '21 at 15:18
0

querySelectorAll returns a NodeList not an array.

You'll need to convert it to an array

var arr = Array.prototype.slice.call(x);
var divs = arr.filter(el => { return el.tagName == 'DIV' });
CAOakley
  • 1,142
  • 1
  • 10
  • 8
  • 1
    how does this answers the *"how to replicate jQuery's `.filter()`"* – Roko C. Buljan May 29 '18 at 20:21
  • Sorry, for assuming knowledge here. I updated my answer to be more complete. I figured converting the NodeList to Array would allow the user to understand .filter is now available. Anywho, now with the .filter() function you can sort by tag name. – CAOakley May 29 '18 at 20:28
  • `Array.prototype` can be used in place of `[]` to avoid the unnecessary array instantiation. – Taplar May 29 '18 at 20:30
  • 1
    what about `const arr = [...document.querySelectorAll("selector")].filter(` etc – Roko C. Buljan May 29 '18 at 20:32
  • Also, if you like shorthands, `y` is quite odd. `el` on the other hand is well known... – Roko C. Buljan May 29 '18 at 20:33
0

A very naive approach:

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)
  };
}

Usable as:

const menus = get(".menu");
for(const el of menus) console.log(el);
console.log(menus.filter("div").get(0));
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151