Chew Kah Meng
Chew Kah Meng

Reputation: 227

How to show x and y coordinates of XY chart with mouseover using d3.js

I am trying to display x and y coordinates of my data points when I mouseover them on my XY chart. The link to my fiddle can be found here. I followed the example from here but I don't know why the tooltip isn't showing when I mouseover my points on my XY chart. The code is as follows, any help is greatly appreciated!

  var data = [ {x: 0, y: 0}, {x: 5, y: 30}, {x: 10, y: 40},
              {x: 15, y: 60}, {x: 20, y: 70}, {x: 25, y: 100} ];

  const margin = {
    left: 20,
    right: 20,
    top: 20,
    bottom: 80
  };

  const svg = d3.select('svg');
  svg.selectAll("*").remove();

  const width = 200 - margin.left - margin.right;
  const height = 200 - margin.top - margin.bottom;

  const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);


  var x = d3.scaleLinear()
          .domain([0, d3.max(data, function(d){ return d.x; })])
          .range([0,width])
          .nice();

  var y = d3.scaleLinear()
          .domain([0, d3.max(data, function(d){ return d.y; })])
          .range([0,height])
          .nice();

  const xAxis = d3.axisTop()
              .scale(x)
              .ticks(5)
              .tickPadding(5)
              .tickSize(-height)

  const yAxis = d3.axisLeft()
              .scale(y)
              .ticks(5)
              .tickPadding(5)
              .tickSize(-width);

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", `translate(20,${height-margin.top-60})`)
    .call(xAxis);

  svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(20,20)")
    .call(yAxis);

  var lineFunction = d3.line()
  .x(function(d) {return x(d.x); })
  .y(function(d) {return y(d.y); })
  .curve(d3.curveLinear);

  var tooltip = d3.select("svg")
      .append("div")
      .style("opacity", 0)
      .attr("class", "tooltip")
      .style("background-color", "white")
      .style("border", "solid")
      .style("border-width", "1px")
      .style("border-radius", "5px")
      .style("padding", "10px")

  // A function that change this tooltip when the user hover a point.
  // Its opacity is set to 1: we can now see it. Plus it set the text and position of tooltip depending on the datapoint (d)
  var mouseover = function(d) {
    tooltip
      .style("opacity", 1)
  }

  var mousemove = function(d) {
    tooltip
      .html("x: " + d.x + "<br/>" + "y: " + d.y)
      .style("left", (d3.mouse(this)[0]+90) + "px")
      .style("top", (d3.mouse(this)[1]) + "px")
  }

  // A function that change this tooltip when the leaves a point: just need to set opacity to 0 again
  var mouseleave = function(d) {
    tooltip
      .transition()
      .duration(200)
      .style("opacity", 0)
  }

  //defining and plotting the lines
  var path = g.append("path")
              .attr("class", "path1")
              .attr("id", "blueLine")
              .attr("d", lineFunction(data))
              .attr("stroke", "blue")
              .attr("stroke-width", 2)
              .attr("fill", "none")
              .attr("clip-path", "url(#clip)");

        // plot a circle at each data point
        g.selectAll(".dot")
            .data(data)
            .enter().append("circle")
            .attr("cx", function(d) { return x(d.x); } )
            .attr("cy", function(d) { return y(d.y); } )
            .attr("r", 3)
            .attr("class", "blackDot")
            .attr("clip-path", "url(#clip)")
            .on("mouseover", mouseover )
            .on("mousemove", mousemove )
            .on("mouseleave", mouseleave )

Upvotes: 0

Views: 1439

Answers (2)

StackSlave
StackSlave

Reputation: 10617

Here is how you find a point on a canvas.

/* js/external.js */
//<![CDATA[
var doc, bod, I; // for use on other loads
addEventListener('load', function(){
doc = document; bod = doc.body;
I = function(id){
  return doc.getElementById(id);
}
var canvas = I('canvas');
canvas.onmousemove = function(e){
  var cb = canvas.getBoundingClientRect(), x = e.clientX-cb.left, y = e.clientY-cb.top;
  console.log('x:'+x+'; y:'+y);   
}
canvas.ontouchmove = function(e){
  var cb = canvas.getBoundingClientRect(), z = e.touches[0], x = z.clientX-cb.left, y = z.clientY-cb.top;
  console.log('x:'+x+'; y:'+y);
}
});
//]]>
/* css/external.css */
*{
  box-sizing:border-box; padding:0; margin:0;
}
html,body{
  width:100%; height:100%;
}
body{
  background:#ccc;
}
#canvas{
  display:block; width:100%; height:100%; background:#fff; margin:0 auto;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
  <head>
    <meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1' />
    <title>Test Template</title>
    <link type='text/css' rel='stylesheet' href='css/external.css' />
    <script type='text/javascript' src='js/external.js'></script>
  </head>
<body>
  <canvas id='canvas'></canvas>
</body>
</html>

Upvotes: 0

willi123yao
willi123yao

Reputation: 144

A <div> tag cannot exist within a <svg> tag, therefore the tooltip element will not be rendered.

I would suggest you to modify the .select(...) function of the accessor for the elements. Instead of selecting the svg element, you should select the body element and append it to there.

Furthermore, to render the div tooltip above the graph, you will need to set the position of the div element to be absolute, as shown below:

var tooltip = d3.select("body")
      .append("div")
      .style("opacity", 0)
      .attr("class", "tooltip")
      .style("background-color", "white")
      .style("border", "solid")
      .style("border-width", "1px")
      .style("border-radius", "5px")
      .style("padding", "10px")
      .style("position", "absolute")


It should work as expected, and here is a fiddle link with the updated code.

Upvotes: 1

Related Questions