Question

I am working on a d3 sample http://bost.ocks.org/mike/nations/:

enter image description here

I am trying to add tooltip of the json data on the corresponding circles as and when i move the cursor over the years. However it is showing only the first year's value. It is not displaying the data corresponding to the year selected.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Mock up Bubble chart DV</title>
<style>

@import url(../style.css?20120427);

#chart {
  margin-left: -40px;
  height: 506px;
}

text {
  font: 10px sans-serif;
}

.dot {
  stroke: #000;
}

.axis path, .axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.label {
  fill: #777;
}

.year.label {
  font: 500 96px "Helvetica Neue";
  fill: #ddd;
}

.year.label.active {
  fill: #aaa;
}

.overlay {
  fill: none;
  pointer-events: all;
  cursor: ew-resize;
}

</style>

<header>
</header>

<h1>Mock up of Bubble chart DV</h1>

<p id="chart"></p>
<input type="submit" value="Start" onclick=start();>
<input type="submit" value="Stop" onclick=stop();>

<script src="http://d3js.org/d3.v2.js?2.8.1"></script>
<script>

// Various accessors that specify the four dimensions of data to visualize.
function x(d) { return d.checkins; }
function y(d) { return d.Checkintimes; }
function radius(d) { return d.teamsize; }
function color(d) { return d.region; }
function key(d) { return d.name; }
var b = false;
var svg = d3.select("#chart");
var thisyear=2000;
var parsedData;
var chksvg = d3.select("svg");



function start()
{

if(chksvg.empty())
{

    //alert("not intialised");

     document.getElementById('chart').innerHTML="";

      // Chart dimensions.
    var margin = {top: 29.5, right: 29.5, bottom: 29.5, left: 59.5},
    width = 960 - margin.right,
    height = 500 - margin.top - margin.bottom;

    // Various scales. These domains make assumptions of data, naturally.
    //var xScale = d3.scale.log().domain([300, 1e5]).range([0, width]),
    var xScale = d3.scale.log().domain([10, 3000]).range([0, width]),

    yScale = d3.scale.linear().domain([10, 85]).range([height, 0]),
    radiusScale = d3.scale.sqrt().domain([0, 40]).range([5, 40]),
    colorScale = d3.scale.category10();

    // The x & y axes.
    var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(12, d3.format(",d")),
    yAxis = d3.svg.axis().scale(yScale).orient("left");



    // Create the SVG container and set the origin.
    var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // Add the x-axis.
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    // Add the y-axis.
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

    // Add an x-axis label.
    svg.append("text")
        .attr("class", "x label")
        .attr("text-anchor", "end")
        .attr("x", width)
        .attr("y", height - 6)
     .text("Avg checkins/dev");

// Add a y-axis label.
svg.append("text")
    .attr("class", "y label")
    .attr("text-anchor", "end")
    .attr("y", 6)
    .attr("dy", ".75em")
    .attr("transform", "rotate(-90)")
    .text("Mean Time between successful builds(hrs)");

// Add the year label; the value is set on transition.
var label = svg.append("text")
    .attr("class", "year label")
    .attr("text-anchor", "end")
    .attr("y", height - 24)
    .attr("x", width)
    .text(2000);



}
else
{

//enableInteraction();

}

//alert("Start Clicked");
// Load the data.

d3.json("bubble_chart_new.json", function(nations) {



  // A bisector since many nation's data is sparsely-defined.
  var bisect = d3.bisector(function(d) { return d[0]; });
  //console.log(bisect);
  // Add a dot per nation. Initialize the data at 2000, and set the colors.


  var dot = svg.append("g")
      .attr("class", "dots")
    .selectAll(".dot")
      .data(interpolateData(2000))
    .enter().append("circle")
      .attr("class", "dot")
      .style("fill", function(d) { return colorScale(color(d)); })
      .call(position)
      .sort(order);

  // Add a title.
  //dot.append("title").text(function(d) { return d.name});

  // Add an overlay for the year label.
  var box = label.node().getBBox();

  var overlay = svg.append("rect")
        .attr("class", "overlay")
        .attr("x", box.x)
        .attr("y", box.y)
        .attr("width", box.width)
        .attr("height", box.height)
        .on("mouseover", enableInteraction);

  // Start a transition that interpolates the data based on year.
  svg.transition()
      .duration(30000)
      .ease("linear")
      .tween("year", tweenYear)
      .each("end", enableInteraction);

  // Positions the dots based on data.
  function position(dot) {
    dot.attr("cx", function(d) { return xScale(x(d)); })
       .attr("cy", function(d) { return yScale(y(d)); })

       .attr("r", function(d) { return radiusScale(radius(d)); });

  }

  // Defines a sort order so that the smallest dots are drawn on top.
  function order(a, b) {
    return radius(b) - radius(a);
  }

  // After the transition finishes, you can mouseover to change the year.
  function enableInteraction() {


    var yearScale = d3.scale.linear()
        .domain([2000, 2009])
        .range([880,895])
        .clamp(true);

    // Cancel the current transition, if any.
    svg.transition().duration(0);

    overlay
        .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("mousemove", mousemove)
        .on("touchmove", mousemove);

    function mouseover() {
      label.classed("active", true);
    }

    function mouseout() {
      label.classed("active", false);
    }

    function mousemove() {

      //console.log(yearScale.invert(d3.mouse(this)[0]));
      displayYear(yearScale.invert(d3.mouse(this)[0]));
       dot.append("title").text(function(d,i) { return d.name + ": " + d.checkins + ":" +  d.teamsize});
    }
  }

  // Tweens the entire chart by first tweening the year, and then the data.
  // For the interpolated data, the dots and label are redrawn.
  function tweenYear() {

    var year = d3.interpolateNumber(thisyear, 2009);
    //console.log(year);
    //var year = d3.interpolateRound(thisyear, 2009);

    return function(t) { displayYear(year(t)); };
  }

  // Updates the display to show the specified year.
  function displayYear(year) {
    thisyear=year;


    dot.data(interpolateData(year), key)
    .call(position)
    .sort(order);
    label.text(Math.round(year));


)

  }

  // Interpolates the dataset for the given (fractional) year.
  function interpolateData(year) {

    var yeartest= year;    
    return nations.map(function(d,i) {



      return {


        name: d.name,
        region: d.region,
        checkins: interpolateValues(d.checkins, year),
        teamsize: interpolateValues(d.teamsize, year),
        Checkintimes: interpolateValues(d.Checkintimes, year)

      };

       //console.log(d.name + d.teamsize + d.checkins);


    });
  }

  // Finds (and possibly interpolates) the value for the specified year.
  function interpolateValues(values, year) {
    var i = bisect.left(values, year, 0, values.length - 1),
        a = values[i];
    if (i > 0) {
      var b = values[i - 1],
          t = (year - a[0]) / (b[0] - a[0]);


          /*console.log("year = " + year);
        console.log("a[0] = " +  a[0]);
        console.log("b[0] = " +  b[0]);

        console.log("t value = " + t);
        console.log("a[1] value = " + a[1]);
       console.log("return value = " + a[1] * (1 - t) + b[1] * t);*/

      //return a[1] * (1 - t) + b[1] * t;
       return a[1]* (1 - t) + b[1] * t ;

    }
    return a[1];
  }
});

}


function stop()
{

  d3.selectAll("*").transition().delay(0);


//alert("stop Clicked");
}

</script>

json file contains the following:

[ {

"name":"Search&Navigator",
"region":"IPScience",
"checkins":[[2000,100],[2001,200],[2002,300],[2003,275],[2004,222],[2005,280],[2006,281],[2007,400],[2008,55],[2009,300]],
"teamsize":[[2000,10],[2001,7],[2002,7],[2003,12],[2004,5],[2005,3],[2006,10],[2007,12],[2008,12],[2009,10]],
"Checkintimes":[[2000,40],[2001,50],[2002,60],[2003,50],[2004,40],[2005,30],[2006,30],[2007,35],[2008,30],[2009,30]]

} ]

Was it helpful?

Solution

All you need to do is, in addition to appending the title initially, update it when the displayed year changes. That is, change

dot.data(interpolateData(year), key)
   .call(position)
   .sort(order);

to

dot.data(interpolateData(year), key)
   .call(position)
   .sort(order)
   .each(function() {
     d3.select(this).select("title")
       .text(function(d,i) { return d.name + ": " + d.checkins + ":" +  d.teamsize});
   });

in the displayYear function. The slightly awkward call to .each() is needed because the data is bound to the circles and not the title elements -- using .select() copies the data.

You can also delete

dot.append("title").text(function(d,i) { return d.name + ": " + d.checkins + ":" +  d.teamsize});

from the mousemove function, as this will add a new title element every time the mouse is moved. Instead, add .append("title") in the definition of dot.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top