Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
177 views
in Technique[技术] by (71.8m points)

javascript - Mouseout on element grouping fires before leaving bounding area in D3

I'm trying to add a click and mouseout handler to a group of circles and text. The click handler works fine, but the mouse out seems to fire even if I haven't left the circleGroup area (shown below).

Note: there are multiple of these circle groups which are added in a grid.

enter image description here

Here is the SVG as it appears in the browser:

enter image description here

The code to produce the circleGroup, containing an outer green-ish circle, an inner white circle, and a text element, is as follows:

let circleGroup = leftPanelGroup.append('g');

let outerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .attr("r", 15);

let innerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .style('stroke', '#fff')
    .attr("r", 7);

let text = circleGroup.append('text')
    .style('color', '#fff')
    .attr("x", x)
    .attr("y", y - 25)
    .style('fill', '#fff')
    .attr("font-size", 12)
    .attr('font-weight', 'bold')
    .attr("font-family", "sans-serif")
    .attr('id', 'circle-text')
    .style("text-anchor", "middle")
    .text('Thank you');

...

On click anywhere within the circleGroup, the circleClick should fire. This works fine. The issue is, the circleMouseout function seems to randomly fire even if I haven't yet left the bounding area of the circleGroup:

circleGroup.on('click', circleClick).on('mouseout', circleMouseout);

function circleClick() {
    // Do something
}
function circleMouseout() {
    // Do something else
}

The output HTML in console shows the area of the <g> svg group element. I'd expect that click anywhere in this highlighted area would fire the click event, and only when I mouse out of this highlighted area would the mouseout event fire. Again, the click works fine, the mouseout does not.

enter image description here

<g>
   <circle cx="252.99037499999997" cy="340.938" r="15" style="fill: rgb(108, 160, 123);"> 
   </circle>
   <circle cx="252.99037499999997" cy="340.938" r="7" style="fill: rgb(108, 160, 123); stroke: rgb(255, 255, 255);">
   </circle>
   <text x="252.99037499999997" y="315.938" font-size="12" font-weight="bold" font-family="sans-serif" id="circle-text" style="color: rgb(255, 255, 255); fill: rgb(255, 255, 255); text-anchor: middle;">Thank you
   </text>
</g>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The bounding box of a g doesn't affect where a mouse interacts with it.The mouse only interacts with the "interaction area" of an element. This is generally the stroke or fill of rendered elements. So, whenever your mouse leaves the circles or the text, you trigger the mouseout event, not when you leave the bounding box of the parent g.

If you want the bounding box of the parent g to interact with the mouse, then we need to add a new rectangle. We can extract the g's bbox and use this to draw a new rectangle over the bounding box of the g. This rectangle can be given the fill of none to make it invisible, but also given a pointer-events property of all to ensure it interacts with the mouse:

let boundingBox = circleGroup.append('rect')
  .each(function() {
     var bbox = this.parentNode.getBBox();  // get parent `g` bounding box
     d3.select(this)
       .attr("width", bbox.width)           // size rect based on bounding box.
       .attr("height", bbox.height)
       .attr("x", bbox.x)
       .attr("y", bbox.y)
       .attr("fill","none")
       .attr("pointer-events","all")
  })

Now we can assign event listeners to the g, or the rect for that matter, and the entire g bounding box will respond to mouse events:

let circleGroup = d3.select("body")
  .append("svg")
  .append("g");

let x = 50;
let y = 50;
let color = "steelblue";

let outerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .attr("r", 15);

let innerCircle = circleGroup.append("circle")
    .attr("cx", x)
    .attr("cy", y)
    .style('fill', color)
    .style('stroke', '#fff')
    .attr("r", 7);

let text = circleGroup.append('text')
    .attr("x", x)
    .attr("y", y - 25)
    .style('fill', color)
    .attr("font-size", 12)
    .attr('font-weight', 'bold')
    .attr("font-family", "sans-serif")
    .attr('id', 'circle-text')
    .style("text-anchor", "middle")
    .text('Thank you');

let boundingBox = circleGroup.append('rect')
  .each(function() {
     var bbox = this.parentNode.getBBox();
     d3.select(this)
       .attr("width", bbox.width)
       .attr("height", bbox.height)
       .attr("x", bbox.x)
       .attr("y", bbox.y)
       .attr("fill","none")
       .attr("pointer-events","all")
  })

circleGroup.on("mouseover", function() {
     console.log("mouseover");
  }).on("mouseout", function() {
     console.log("mouseout");
  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...