Javascript group elements with identical classnames

77 views Asked by At

I want to select all elements with class name find-me and group them in separate arrays (or NodeLists) based on their other classes, if idential. Here is an example:

<div class="find-me one two">
  Element 1
</div>
<div class="find-me one two">
  Element 2
</div>
<div class="find-me one two three">
  Element 3
</div>
<div class="find-me one two three">
  Element 4
</div>
<div class="find-me one two four">
  Element 5
</div>
<div class="find-me one two">
  Element 6
</div>
<div class="one two">
  Element 7
</div>

My goal is to have:

  • One array or NodeList with elements 1, 2 and 6 (find-me, one, two)
  • One array or NodeList with elements 3 and 4 (find-me, one, two, three)
  • One array or NodeList with element 5 (find-me, one, two, four)
  • Element 7 should not be taken into account since it doesn't have the find-me class

I suppose I have to use document.querySelectorAll('.find-me') to first get a NodeList of all the elements I am interested in.

But then? I was thinking I could compare the list of classes from the current element with the previous one but how can I keep track of what element goes to what group? Any idea how to achieve that?

3

There are 3 answers

2
Hao Wu On BEST ANSWER

Sounds like a good opportunity to use Object.groupBy, but be aware of its poor coverage

const groups = Object.groupBy(document.querySelectorAll('.find-me'), e => e.classList);

console.log(groups);
<div class="find-me one two">
  Element 1
</div>
<div class="find-me one two">
  Element 2
</div>
<div class="find-me one two three">
  Element 3
</div>
<div class="find-me one two three">
  Element 4
</div>
<div class="find-me one two four">
  Element 5
</div>
<div class="find-me one two">
  Element 6
</div>
<div class="one two">
  Element 7
</div>

Using the same concept, you can write a reduce function to achieve the same goal, also the order/repetitive classes doesn't matter:

const groups = [...document.querySelectorAll('.find-me')].reduce((acc, cur) => {
  const key = [...cur.classList].sort();
  acc[key] ??= [];
  // use the following code instead of the line above if you need to support IE11
  // acc[key] || (acc[key] = []);
  acc[key].push(cur);
  return acc;
}, {});

console.log(groups);
<div class="find-me one two">
  Element 1
</div>
<div class="two find-me one one">
  Element 2
</div>
<div class="find-me one two three">
  Element 3
</div>
<div class="find-me one three two ">
  Element 4
</div>
<div class="find-me one two four">
  Element 5
</div>
<div class="find-me one two">
  Element 6
</div>
<div class="one two">
  Element 7
</div>

0
Kastet6398 On

You can use this code:

const findMeElements = document.querySelectorAll('.find-me');

const groupedElements = {};

findMeElements.forEach(element => {
    const currentClasses = Array.from(element.classList);

    const otherClasses = currentClasses.filter(className => className !== 'find-me');

    otherClasses.sort();

    const key = otherClasses.join(',');

    if (groupedElements[key]) {
        groupedElements[key].push(element);
    } else {
        groupedElements[key] = [element];
    }
});

console.log(groupedElements);
<div class="find-me one two">
  Element 1
</div>
<div class="find-me one two">
  Element 2
</div>
<div class="find-me one two three">
  Element 3
</div>
<div class="find-me one two three">
  Element 4
</div>
<div class="find-me one two four">
  Element 5
</div>
<div class="find-me one two">
  Element 6
</div>
<div class="one two">
  Element 7
</div>

1
Ermi On

You can iterate through each element in the NodeList then for each element, extract its classList and convert it into a string so you can use it as a key in an object. If an array already exists for that particular combination of classes, push the element into that array. If not, create a new array (or NodeList) and add the element to it.

Demo:

const elements = document.querySelectorAll('.find-me');
const groupedElements = {};

elements.forEach(element => {
  const classList = Array.from(element.classList).sort().join(' ');
  if (groupedElements[classList]) {
    groupedElements[classList].push(element);
  } else {
    groupedElements[classList] = [element];
  }
});

// Logging the grouped elements in a more readable format
for (const classes in groupedElements) {
  const elements = groupedElements[classes];
  console.log(`Elements with classes "${classes}":`);
  elements.forEach((element, index) => {
    console.log(`  Element ${index + 1}: ${element.textContent.trim()}`);
  });
}
<div class="find-me one two">
  Element 1
</div>
<div class="find-me one two">
  Element 2
</div>
<div class="find-me one two three">
  Element 3
</div>
<div class="find-me one two three">
  Element 4
</div>
<div class="find-me one two four">
  Element 5
</div>
<div class="find-me one two">
  Element 6
</div>
<div class="one two">
  Element 7
</div>