-1

I have an array of objects.These objects contain a set of attributes named "x" and "y". Now as i iterate through the array,i append circle for each object into array taking these coordinates as center of the circle.I am trying to attach a tooltip on mouse hover of each circle which displays the x and y coordinate of the circle. The two problems i am facing is 1.How to append a div element to each circle 2.How to get hover event for the circle? Please help?

   <!doctype html>
    <html>
      <head>
        <title>D3 Basics</title>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
      </head>
      <body>
        <script>
          var data=[
            {"x":"100","y":"20"},
            {"x":"102","y":"22"},
            {"x":"200","y":"30"},
            {"x":"500","y":"40"},
            {"x":"500","y":"30"}
          ];

          var svgHeight=800;
          var svgWidth=800;
          var margin=50;

          var divelement=d3.select("body")
            .append("div")
            .attr("height",svgHeight)
            .attr("width",svgWidth)
            .attr("style","border:1px solid black;");

          var svgElement=divelement.append("svg")
            .attr("height",svgHeight)
            .attr("width",svgWidth);

          var boxGroupElement=svgElement.append("g")
            .attr("transform","translate("+margin+","+margin+")");


          //appending data circle points        
          for (var a=0; a<data.length; a++) {
           boxGroupElement.append("circle")
              .attr("cx",data[a].x)
              .attr("cy",data[a].y)
              .attr("r","3")
              .attr("fill","yellow")
              .attr("stroke","blue");
          }         
        </script>
      </body>
    </html>
Jason
  • 676
  • 1
  • 12
  • 34
  • Possible duplicate of [D3: show data on mouseover of circle](http://stackoverflow.com/questions/10805184/d3-show-data-on-mouseover-of-circle) – thatOneGuy Jun 06 '16 at 10:11

2 Answers2

3

I have marked this as duplicate but there can be something to learn here.

Where you are creating the circles :

for (var a=0; a<data.length; a++) {
           boxGroupElement.append("circle")
              .attr("cx",data[a].x)
              .attr("cy",data[a].y)
              .attr("r","3")
              .attr("fill","yellow")
              .attr("stroke","blue");
          } 

This is totally wrong if you are using D3. If you put a mouseover event on this like you normally would with D3 to console log the data like so :

.on('mouseover',function(d){ console.log(d);});

This won't work as there is no data appended to your selection. A work around would be the following :

 var circles = boxGroupElement.selectAll('circle')
   .data(data)
   .enter()
   .append('circle')
   .attr("cx", function(d) {
     return d.x
   })
   .attr("cy", function(d) {
     return d.y
   })
   .attr("r", "3")
   .attr("fill", "yellow")
   .attr("stroke", "blue")

Now for the tooltip. I have referenced this above : Show data on mouseover of circle

The second answer precisely. Here is how it works :

Add a div for your tooltip to sit and set visibility to hidden (so it can become visible on mouseover) :

 var tooltip = d3.select("body")
   .append("div")
   .style("position", "absolute")
   .style("z-index", "10")
   .style("visibility", "hidden")
   .text("a simple tooltip");

And on mouseover a node show data. Here I have shown the position. Bare in mind, this way wouldn't work if you stuck with your original way of creating the circles as you wouldn't be able to find the data :

.on("mouseover", function(d) {
     tooltip.text("Pos : " + d.x + ' : ' + d.y);

     return tooltip.style("visibility", "visible");
   })
   .on("mousemove", function() {
     return tooltip.style("top",
       (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
   })
   .on("mouseout", function() {
     return tooltip.style("visibility", "hidden");
   });

Working fiddle : https://jsfiddle.net/thatOneGuy/7qt1aoan/2/

EDIT

If you are adamant you want to keep it like you have the following could be a work around :

 .on("mouseover", function(d, i) {
       tooltip.text("Pos : " + data[i].x + ' : ' + data[i].y);

       return tooltip.style("visibility", "visible");
     })
     .on("mousemove", function() {
       return tooltip.style("top",
         (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
     })
     .on("mouseout", function() {
       return tooltip.style("visibility", "hidden");
     });

Updated fiddle for this workaround : https://jsfiddle.net/thatOneGuy/7qt1aoan/3/

Again on the mouseover, you can't use data[a].x as this will always return the last element of the data, closures etc, so I use data[i].x which gives you the current circle you are mousing over :)

Community
  • 1
  • 1
thatOneGuy
  • 9,977
  • 7
  • 48
  • 90
-1

You need to add mouseover event to boxGroupElement.append("circle") something like this

boxGroupElement.append("circle")
                  .attr("cx",data[a].x)
                  .attr("cy",data[a].y)
                  .attr("r","3")
                  .attr("fill","yellow")
                  .attr("stroke","blue").on("mouseover", function(d) {
                    div.transition().duration(100).style("opacity", .9);
                    div.html("My Tooltip" + "<br/>" +d )
                    .style("left", (d3.event.pageX) + "px")
                    .style("top",(d3.event.pageY - 28) + "px")
                   .attr('r', 8);
                   d3.select(this).attr('r', 8)})
                  .on("mouseout", function(d) {
                   div.transition().duration(600).style("opacity", 0)
                   d3.select(this).attr('r', 3);
                   });

Here is a working example.

SiddP
  • 1,603
  • 2
  • 14
  • 36