JeffA JeffA - 3 months ago 23
Javascript Question

d3 javascript clicking a line to change line color - changes all lines

I have a force-directed graph and want to change the color of a line between 2 nodes when I click on the line. However, the following code changes the color of all the lines, not just the one I clicked.

My css for the line and the clicked line are:

.link {
fill: none;
stroke: #666;
stroke-width: 2px;
cursor: pointer;
}

.link--clicked {
fill: none;
stroke: red;
stroke-width: 2px;
cursor: pointer;
}


I call a function when clicking on the line using:

var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; })
.on("click", function(d) { edge_clicked(d); });

function edge_clicked(d) {
d3.select("path").classed("link--clicked", false); //remove color class of any previously clicked link
var clicked = d3.select(this); //select clicked element
path.classed("link--clicked", true); //set class of clicked link
}


I suspect it has to do with how I am calling the path.classed command which affects all paths/lines not just the clicked line. However, I can't figure out the syntax for the selected line.

Any help is appreciated.

Answer

The main problem in your code is the use of this. Inside the function edge_clicked, this points to the window, not the clicked element.

To solve this, first we pass that clicked element to the function edge_clicked:

var path = svg.append("g").selectAll("path")
    .data(force.links())
    .enter().append("path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; })
    .on("click", function(d) { edge_clicked(this);});

So, we don't need this anymore inside the function edge_clicked.

Now we change the function:

function edge_clicked(elem) {    
    d3.selectAll(".link").classed("link--clicked", false);//selectAll instead of select
    var clicked = d3.select(elem);
    clicked.classed("link--clicked", true);//set class of clicked link
}

Notice that we firs select All (not select) links, and then we select only the clicked one.