I have a force-directed graph where on node mouseover the elements which are not connected to the current node are faded out, based on the following example. The problem is that if the cursor is moved fast and it crosses a link which is not part of the target node's connections, it is still shown in full opacity since the crossed link mouseover is triggered on the way, but apparently does not have time to complete its mouseout function. For instance you can see this:
In the picture the mouse is moved fast from top to bottom and the cursor is stopped at the HSA-MIR-424. The connections to the target node are shown correctly but also three extra links are visible since their mouseover is triggered when the cursor crosses them. If I repeat the top-to-bottom motion slowly to the same node or move bottom-to-top to the node such problem is not seen (since there are no links to be crossed in that direction, in the picture).
How can this problem be avoided?
Relevant parts of the code, link mouseover/mouseout:
.on("mouseover", function(d) {
var selection = d3.select(this);
var initialWidth = Number( selection.style("stroke-width") );
selection.transition().style("stroke-width", initialWidth + Number(4) )
.style("stroke-opacity", 1.0);
//.style("stroke", linkOverColor);
} )
.on("mouseout", function(d) {
var selection = d3.select(this);
selection.transition().style("stroke-width", getLinkStroke( selection.data()[0] ) )
.style("stroke-opacity", conf.link_def_opacity);
})
Node mouseover/mouseout:
// display closest neighbors and fade others out
.on("mouseover", fade(0.10) )
// return to default view
.on("mouseout", fade(conf.node_def_opacity) );
The fading function:
function fade(opacity) {
return function(d) {
d3.event.stopPropagation();
var thisOpacity;
// return to default view
if( opacity === conf.node_def_opacity )
{
d3.selectAll('marker > path').transition().style("opacity", 1);
nodeGroup.transition().style("opacity", conf.node_def_opacity);
link.style("stroke-opacity", conf.link_def_opacity);
}
else // fade not-neighborhood away
{
d3.selectAll('marker > path').transition().style("opacity", 0);
// d3.selectAll('marker > path').transition().style('display', 'none');
nodeGroup.transition().style("opacity", function(o)
{
thisOpacity = isConnected(d, o) ? conf.node_def_opacity : opacity;
return thisOpacity;
});
link.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? conf.link_def_opacity : opacity;
});
}
}
}