简体   繁体   中英

How can I keep a d3 mouseover open while my mouse is over the tooltip?

I have a d3 scatterplot. I have a tooltip which shows when I mouseover a point. I want to do two things.

1) I want the mouseover to stay open as long as my mouse is either over the point or the tooltip. 2) I want to put a clickable link in the tooltip. I believe that #1 is required in order to make this work.

How can I do this?

Here is my code: https://github.com/laran/eisenhower/blob/master/components/plot/scatterplot.js

One idea may be to create a delayed transition on the mouseout of the circle to allow the user time to mouse to the tooltip. If they mouseover the circle in time, you cancel the transition and hide the tooltip on mouseout of tooltip div:

// create tooltip
var tip = d3.select('body')
  .append('div')
  .attr('class', 'tip')
  .html('I am a tooltip...')
  .style('border', '1px solid steelblue')
  .style('padding', '5px')
  .style('position', 'absolute')
  .style('display', 'none')
  .on('mouseover', function(d, i) {
    tip.transition().duration(0);  // on mouse over cancel circle mouse out transistion
  })
  .on('mouseout', function(d, i) {
    tip.style('display', 'none');  // on mouseout hide tip
  });

 ...

 // mouseover and out of circle
.on('mouseover', function(d, i) {
    tip.transition().duration(0); // cancel any pending transition
    tip.style('top', y(d.y) - 20 + 'px');
    tip.style('left', x(d.x) + 'px');
    tip.style('display', 'block');
  })
  .on('mouseout', function(d, i) {
    tip.transition()
    .delay(500)
    .style('display', 'none'); // give user 500ms to move to tooltip
  });

Here's a quick example .

 <!DOCTYPE html> <html> <head> <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> <script> // data that you want to plot, I've used separate arrays for x and y values var data = [{ x: Math.random() * 10, y: Math.random() * 10 }, { x: Math.random() * 10, y: Math.random() * 10 }, { x: Math.random() * 10, y: Math.random() * 10 }, { x: Math.random() * 10, y: Math.random() * 10 } ]; xdata = [5, 10, 15, 20], ydata = [3, 17, 4, 6]; // size and margins for the chart var margin = { top: 20, right: 15, bottom: 60, left: 60 }, width = 500 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; var x = d3.scale.linear() .domain([0, 10]) .range([0, width]); var y = d3.scale.linear() .domain([0, 10]) .range([height, 0]); var tip = d3.select('body') .append('div') .attr('class', 'tip') .html('I am a tooltip...') .style('border', '1px solid steelblue') .style('padding', '5px') .style('position', 'absolute') .style('display', 'none') .on('mouseover', function(d, i) { tip.transition().duration(0); }) .on('mouseout', function(d, i) { tip.style('display', 'none'); }); var chart = d3.select('body') .append('svg') .attr('width', width + margin.right + margin.left) .attr('height', height + margin.top + margin.bottom) .attr('class', 'chart') var main = chart.append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') .attr('width', width) .attr('height', height) .attr('class', 'main') // draw the x axis var xAxis = d3.svg.axis() .scale(x) .orient('bottom'); main.append('g') .attr('transform', 'translate(0,' + height + ')') .attr('class', 'main axis date') .call(xAxis); // draw the y axis var yAxis = d3.svg.axis() .scale(y) .orient('left'); main.append('g') .attr('transform', 'translate(0,0)') .attr('class', 'main axis date') .call(yAxis); // draw the graph object var g = main.append("svg:g"); g.selectAll("scatter-dots") .data(data) .enter().append("svg:circle") .attr("cy", function(d) { return y(dy); }) .attr("cx", function(d, i) { return x(dx); }) .attr("r", 10) .style("opacity", 0.6) .on('mouseover', function(d, i) { tip.transition().duration(0); setTimeout(() => { tip.style('top', y(dy) - 20 + 'px'); tip.style('left', x(dx) + 'px'); tip.style('display', 'block'); }, 500); }) .on('mouseout', function(d, i) { tip.transition() .delay(500) .style('display', 'none'); }) </script> </body> </html> LICENSE

There is probably no easy way to do it.

One option is to nest the tooltip inside the same container as the circle (ie an svg <g> ) and attach the mouse events to that parent, so that when the mouse goes between the tooltip and circle it won't trigger mouseout. That'll make it hard to transition the tooltip between circles, because it would involve detaching it from one parent and attaching to the other.

Probably the simpler option is to attach a mouseover and mouseout events to the tooltip, and set a flag (like isOverTooltip = true or false ) to keep track of where the mouse is. Then check this flag in the mouseout of the circle to determine whether or not to hide the tooltip. In this case, inside the mouseout of the circle, you would want to hide the tooltip from within a setTimeout (and of course only when !isOverTooltip ), in order to allow time for the mouse to travel between circle and tooltip without the tooltip disappearing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM