Mouse manipulation "hit area" in d3.js
I would like to show and hide a node in SVG when mouseover / mouse> the problem is that my form inside the node is a path only 1.5px wide, so it is not easy to hit that area on a mouseover event, which is definitely awkward for users.
I would like to know if there is a way to make this remote area wider using an arbitrary width but invisible to the user?
snippet of code:
link.enter()
.append('g').attr('class', 'link')
.append('line')
.attr('class', 'path')
.attr('marker-end', function(d, i) {
if (i === 2) {
return 'url(#arrow)';
} else {
return null;
}
}).on('mouseover', function(d) {
//alert(JSON.stringify(d));
alert('mouseover');
}).on('mouseout', function(d) {
alert('mouseout');
});
css:
.node .path {
stroke: #878f8f;
stroke-width: 1.5px;
fill:none;
}
source to share
You can add line
in g
a transparent stroke and high stroke width, which will increase the area of contact.
// Subscribe to mouse events on the entire g
gEnter = link.enter()
.append('g')
.attr('class', 'link')
.on('mouseover', function(d) {
//alert(JSON.stringify(d));
alert('mouseover');
}).on('mouseout', function(d) {
alert('mouseout');
});
// Instead of 1 line, append 2 lines inside the g, and make
// one of them transparent and "fat", which will enlarge the
// hit area
lines = gEnter
.selectAll('.path').data(['visible', 'invisible'])
lines.enter()
.append('line')
.attr('class', 'path')
.attr('marker-end', function(d, i, j) {
// j is the parent i
if (j === 2) {
return 'url(#arrow)';
} else {
return null;
}
})
.attr({
// returning null from these functions simply defaults to whatever the
// .path class CSS props are doing
'stroke-width': function(d, i) { return d == 'invisible' ? 10 : null },
'stroke': function(d, i) { return d == 'invisible' ? 'transparent' : null }
})
source to share
How do you define link
here? I couldn't understand your solution, but I stuck with the same idea of adding two lines (one visible and the other invisivle) to the parent element g
. I don't think this is too efficient because I have to call the line coordinates twice (once for the visible line and one for the invisible line). This is what I did:
//define the link element in a parent g
var link = svg.selectAll("g.link")
.data(json.links)
.enter().append("g")
.on("click", linkMouseClick)
.on("mouseover", linkMouseover);
//append a visible child line to parent g
var line = link.append("line")
.attr("class", "link")
.style("stroke-width", "2");
//append a second, fatter line to g and make it invisible
var fatline = link.append("line")
.attr("class", "link")
.attr("style", "stroke:transparent;stroke-width:10px");
//call for line coordinates for both lines
force.on("tick", function() {
line.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
fatline.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
});
This works, but if anyone can suggest improvements that would be great, thanks!
source to share