Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

manipulate mouseover "hit area"in d3.js

Tags:

svg

d3.js

I'd like to show and hide a node in SVG when mouseover / mouseout, the issue is that my shape inside the node is a path with only 1.5px width so is not easy to hit that area in the mouseover event, that's definitely inconvenient to user experience.

I'd like to know if there's a way to make that hit area wider using an arbitrary width, but invisible to the user?

a snippet of my 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');
    });

the css:

.node .path {
  stroke: #878f8f;
  stroke-width: 1.5px;
  fill:none;
}
like image 829
pedrommuller Avatar asked Feb 12 '23 11:02

pedrommuller


2 Answers

You can add another line to the g with transparent stroke and with large stroke-width, which will increase the hit area.

// 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's i
        if (j === 2) {
            return 'url(#arrow)';
        } else {
            return null;
        }
    })
    .attr({
        // returning null from these functions simply defaults to whatever the
        // .path class's CSS props are doing
        'stroke-width': function(d, i) { return d == 'invisible' ? 10 : null },
        'stroke': function(d, i) { return d == 'invisible' ? 'transparent' : null }
    })
like image 56
meetamit Avatar answered Feb 13 '23 23:02

meetamit


How are you defining link here? I could not figure out your solution, but I followed the same idea of appending two lines (one visible and the other invisivle) to a parent g element. I don't think it is too efficient, because I end up having to call for 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; }); 

 });

It works, but if anyone can suggest improvements, that would be great, thanks!

like image 44
user1299024 Avatar answered Feb 14 '23 01:02

user1299024