Reputation: 3923
I have a D3 multi-line graph that uses the legend for onclick
mouseover
and mouseout
events. Clicking on the legend will hide the line. Mousing over the legend will make the line bold and mousing out will put the line back to normal.
The problem is that if I click the legend and then remove the mouse before the D3 transition completes the transition will not finish. If I keep the mouse over the legend long enough for the transition everything works fine.
To test click a legend rectangle and quickly move the mouse out - the line will not disappear.
Fiddle here: https://jsfiddle.net/goodspeedj/5ewLxpre/
The code for the mouse events is below:
.on("click", function(d) {
var selectedPath = svg.select("path." + d.key);
//var totalLength = selectedPath.node().getTotalLength();
if (d.visible === 1) {
d.visible = 0;
} else {
d.visible = 1;
}
rescaleY();
updateLines();
updateCircles();
svg.select("rect." + d.key).transition().duration(500)
.attr("fill", function(d) {
if (d.visible === 1) {
return color(d.key);
} else {
return "white";
}
})
svg.select("path." + d.key).transition().duration(500)
.delay(150)
.style("display", function(d) {
if(d.visible === 1) {
return "inline";
}
else return "none";
})
.attr("d", function(d) {
return line(d.values);
});
svg.selectAll("circle." + d.key).transition().duration(500)
//.delay(function(d, i) { return i * 10; })
.style("display", function(a) {
if(d.visible === 1) {
return "inline";
}
else return "none";
});
})
.on("mouseover", function(d) {
d3.select(this)
.attr("height", 12)
.attr("width", 27)
d3.select("path." + d.key).transition().duration(200)
.style("stroke-width", "4px");
d3.selectAll("circle." + d.key).transition().duration(200)
.attr("r", function(d, i) { return 4 })
// Fade out the other lines
var otherlines = $(".line").not("path." + d.key);
d3.selectAll(otherlines).transition().duration(200)
.style("opacity", 0.3)
.style("stroke-width", 1.5)
.style("stroke", "gray");
var othercircles = $("circle").not("circle." + d.key);
d3.selectAll(othercircles).transition().duration(200)
.style("opacity", 0.3)
.style("stroke", "gray");
})
.on("mouseout", function(d) {
d3.select(this)
.attr("height", 10)
.attr("width", 25)
d3.select("path." + d.key).transition().duration(200)
.style("stroke-width", "1.5px");
d3.selectAll("circle." + d.key).transition().duration(200)
.attr("r", function(d, i) { return 2 })
// Make the other lines normal again
var otherlines = $('.line').not("path." + d.key);
d3.selectAll(otherlines).transition().duration(100)
.style("opacity", 1)
.style("stroke-width", 1.5)
.style("stroke", function(d) { return color(d.key); });
var othercircles = $("circle").not("circle." + d.key);
d3.selectAll(othercircles).transition().duration(200)
.style("opacity", 1)
.style("stroke", function(d) { return color(dimKey(d)); });
});
Thanks in advance.
Upvotes: 0
Views: 1810
Reputation: 1098
You could assign a class to your legend when it's clicked (.clicked), then call setTimeout with an appropriate delay to remove that class once the transition is complete.
When you mouseover or mouseout, first check to see if the legend has the .clicked class. If so, set some delay value as suggested in the other answer, otherwise, proceed without a delay. The advantage of this compared to the other answer is that there would only be a delay if it's needed.
EDIT
If your legend has the class ".legend", modify your code as follows:
.on("click", function(d) {
// Add .clicked class to the legend
$('.legend').addClass('clicked');
// remove clicked class after 750ms. Your duration is 500ms,
// so I'm padding it a bit although you can adjust this as needed
setTimeout(function () { $('.legend').removeClass('clicked') }, 750);
... rest of your function
})
.on("mouseover", function(d) {
// check if legend has been clicked recently and change delay if so
var transitionDelay = 0;
if($('.legend').hasClass('clicked')) transitionDelay = 750;
// your function
d3.select(this)
.attr("height", 12)
.attr("width", 27)
d3.select("path." + d.key).transition().delay(transitionDelay).duration(200)
.style("stroke-width", "1.5px");
... rest of your function
});
Upvotes: 1
Reputation: 786
When you have multiple transitions, one can interrupt another. What is happening with your code is that the onclick transition get interrupted by the mouseout transition. This results in the lines not showing showing up. To fix this, just add delay to your mouseout event, so that it occurs after the onclick event has completed. For example, I made the following changes:
added a delay to line 295:
d3.select("path." + d.key).transition().delay(300).duration(200)
.style("stroke-width", "1.5px");
and on line 244 reduced your onclick delay to 200 from 500, just for this test,
svg.select("path." + d.key).transition().duration(200)
.delay(150)
Upvotes: 1