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);
}
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");
}
});
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With