Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I toggle the class of all elements in a selection?

I am trying to write a function that adds a class to all elements in a selection when those elements do not have that class yet, and vice versa:

function toggleLinksActivity(d) {
  d3.selectAll(".link")
    .filter(l => l.target == d)
    .classed("non-active", l => !l.classed("non-active"));
}

This gives an error that .classed is not a function. The documentation states that .classed should be called on a selection, so I tried changing the last line into !d3.select(l).classed("non-active"), but that does not work either ("t.getAttribute is not a function"). What is going wrong and what is the correct way?

Note: I have already solved my problem with the 2 separate functions underneath, but I find it ugly to split a toggle into 2 parts.

function activateLinks(d) {
  d3.selectAll(".link")
    .filter(l => l.target == d)
    .classed("non-active", false);
}

function deactivateLinks(d) {
  d3.selectAll(".link")
    .filter(l => l.target == d)
    .classed("non-active", true);
}
like image 312
Jeroen Avatar asked May 05 '18 15:05

Jeroen


2 Answers

Gerardo's answer is pointing in the right direction by using Element.classList, but the proposed solution can still be simplified. As it turns out, the classList property already features a .toggle() method, which works as follows:

toggle( String [, force] )
When only one argument is present: Toggle class value; i.e., if class exists then remove it and return false, if not, then add it and return true. When a second argument is present: If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it.

Your function can thus be written as:

function toggleLinksActivity(d) {
  d3.selectAll(".link")
    .each(function(l) {
      if (l.target == d) {
        this.classList.toggle("non-active");
      }
    });
}
like image 199
altocumulus Avatar answered Sep 26 '22 13:09

altocumulus


You need to reselect the ith element and use its classed property:

function toggleLinkActive() {
  d3.selectAll(".link")
    .classed("non-active", (d,i,nodes) => !d3.select(nodes[i]).classed("non-active"));
}

You can also select this if you are using a non-arrow function:

function toggleLinkActive() {
  d3.selectAll(".link")
    .classed("non-active", function() { return !d3.select(this).classed("non-active"); });
}

If you want to avoid reselecting the item, you can use each and store the selection in a variable:

function toggleLinkActive() {
  d3.selectAll(".link")
    .each(function() {
      const item = d3.select(this);
      item.classed("non-active", !item.classed("non-active"));
    });

Or you can use the native this.classList.toggle("non-active") like in @altocumulus' answer.

like image 43
thibautg Avatar answered Sep 25 '22 13:09

thibautg