Search code examples
d3.jstooltip

Tooltip in multiple ring charts d3js


While trying to use the tooltip for each ring in a multiple ring donut chart using d3js , while hovering on the ring it just shows the values for the first child . Below is the code which shows the rings as well as the tooltip . What would I be doing in order to get all the correct item on hover.How do I iterate through the json to get the correct tooltip.

var dataset = [{
  "name": "Population Quater",
  "code": "POP_QUATER",
  "parent": "POP_BY_QUAT",
  "children": [{
    "name": "POP_CYQ1",
    "code": "POP_CYQ1",
    "parent": "POP_QUATER",
    "value": "6772",
    "label": "CYQ1",
    "children": []
  }, {
    "name": "POP_CYQ2",
    "code": "POP_CYQ2",
    "parent": "POP_QUATER",
    "value": "6716",
    "label": "CYQ2",
    "children": []
  }, {
    "name": "POP_CYQ3",
    "code": "POP_CYQ3",
    "parent": "POP_QUATER",
    "value": "6714",
    "label": "CYQ3",
    "children": []
  }, {
    "name": "POP_CYQ4",
    "code": "POP_CYQ4",
    "parent": "POP_QUATER",
    "value": "6703",
    "label": "CYQ4",
    "children": []
  }, {
    "name": "POP_LYQ1",
    "code": "POP_LYQ1",
    "parent": "POP_QUATER",
    "value": "6721",
    "label": "LYQ1",
    "children": []
  }, {
    "name": "POP_LYQ2",
    "code": "POP_LYQ2",
    "parent": "POP_QUATER",
    "value": "6671",
    "label": "LYQ2",
    "children": []
  }, {
    "name": "POP_LYQ3",
    "code": "POP_LYQ3",
    "parent": "POP_QUATER",
    "value": "6708",
    "label": "LYQ3",
    "children": []
  }, {
    "name": "POP_LYQ4",
    "code": "POP_LYQ4",
    "parent": "POP_QUATER",
    "value": "6734",
    "label": "LYQ4",
    "children": []
  }]
}, {
  "name": "Transient Pop",
  "code": "TRANSIENT_POP",
  "parent": "POP_BY_QUAT",
  "label": "Transient Pop",
  "children": [{
    "name": "TRANSIENT_LYQ1",
    "code": "TRANSIENT_LYQ1",
    "parent": "TRANSIENT_POP",
    "value": "54",
    "label": "LYQ1",
    "children": []
  }, {
    "name": "TRANSIENT_LYQ2",
    "code": "TRANSIENT_LYQ2",
    "parent": "TRANSIENT_POP",
    "value": "86",
    "label": "LYQ2",
    "children": []
  }, {
    "name": "TRANSIENT_LYQ3",
    "code": "TRANSIENT_LYQ3",
    "parent": "TRANSIENT_POP",
    "value": "219",
    "label": "LYQ3",
    "children": []
  }, {
    "name": "TRANSIENT_LYQ4",
    "code": "TRANSIENT_LYQ4",
    "parent": "TRANSIENT_POP",
    "value": "191",
    "label": "LYQ4",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ1",
    "code": "TRANSIENT_CYQ1",
    "parent": "TRANSIENT_POP",
    "value": "52",
    "label": "CYQ1",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ2",
    "code": "TRANSIENT_CYQ2",
    "parent": "TRANSIENT_POP",
    "value": "91",
    "label": "CYQ2",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ3",
    "code": "TRANSIENT_CYQ3",
    "parent": "TRANSIENT_POP",
    "value": "222",
    "label": "CYQ3",
    "children": []
  }, {
    "name": "TRANSIENT_CYQ4",
    "code": "TRANSIENT_CYQ4",
    "parent": "TRANSIENT_POP",
    "value": "186",
    "label": "CYQ4",
    "children": []
  }]
}, {
  "name": "Seasonal Pop",
  "code": "SEASONAL_POP",
  "parent": "POP_BY_QUAT",
  "label": "Seasonal Pop",
  "children": [{
    "name": "SEASONAL_LYQ1",
    "code": "SEASONAL_LYQ1",
    "parent": "SEASONAL_POP",
    "value": "2",
    "label": "LYQ1",
    "children": []
  }, {
    "name": "SEASONAL_LYQ2",
    "code": "SEASONAL_LYQ2",
    "parent": "SEASONAL_POP",
    "value": "24",
    "label": "LYQ2",
    "children": []
  }, {
    "name": "SEASONAL_LYQ3",
    "code": "SEASONAL_LYQ3",
    "parent": "SEASONAL_POP",
    "value": "152",
    "label": "LYQ3",
    "children": []
  }, {
    "name": "SEASONAL_LYQ4",
    "code": "SEASONAL_LYQ4",
    "parent": "SEASONAL_POP",
    "value": "55",
    "label": "LYQ4",
    "children": []
  }, {
    "name": "SEASONAL_CYQ1",
    "code": "SEASONAL_CYQ1",
    "parent": "SEASONAL_POP",
    "value": "2",
    "label": "CYQ1",
    "children": []
  }, {
    "name": "SEASONAL_CYQ2",
    "code": "SEASONAL_CYQ2",
    "parent": "SEASONAL_POP",
    "value": "22",
    "label": "CYQ2",
    "children": []
  }, {
    "name": "SEASONAL_CYQ3",
    "code": "SEASONAL_CYQ3",
    "parent": "SEASONAL_POP",
    "value": "161",
    "label": "CYQ3",
    "children": []
  }, {
    "name": "SEASONAL_CYQ4",
    "code": "SEASONAL_CYQ4",
    "parent": "SEASONAL_POP",
    "value": "55",
    "label": "CYQ4",
    "children": []
  }]
}];

var width = 460,
  height = 300,
  cwidth = 25;


var color = d3.scaleOrdinal(d3.schemeCategory20);

var pie = d3.pie()
  .sort(null).value(function(d) {
    return d.value; //since score is the parameter for the pie
  });

var arc = d3.arc();

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

var gs = svg.selectAll("g")
  .data(dataset)
  .enter()
  .append("g")
  .attr("class", "arc").on("mouseover", function() {
    tooltip.style("display", null);
  })
  .on("mouseout", function() {
    tooltip.style("display", "none");
  })

.on("mousemove", function(d, i) {

  tooltip.transition().duration(200)
    .style("opacity", 0.9);
  tooltip.select("div").html(d.children[i].name + ":" + " <strong>" + d.children[i].value + "</strong>")
    .style("position", "fixed")
    .style("text-align", "center")
    .style("width", "120px")
    .style("height", "45px")
    .style("padding", "2px")
    .style("font", "12px sans-serif")
    .style("background", "lightsteelblue")
    .style("border", "0px")
    .style("border-radius", "8px")
    .style("left", (d3.event.pageX) + "px")
    .style("top", (d3.event.pageY - 28) + "px");

});

var tooltip = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0.5);

tooltip.append("rect")
  .attr("width", 30)
  .attr("height", 20)
  .attr("fill", "#ffffff")
  .style("opacity", 0.5);

tooltip.append("div")
  .attr("x", 15)
  .attr("dy", "1.2em")
  .style("text-anchor", "middle")
  .attr("font-size", "1.5em")
  .attr("font-weight", "bold");


var path = gs.selectAll("path")
  .data(function(d, i) {
    return pie(d.children).map(function(payload) {
      return {
        payload: payload,
        parentIndex: i
      }
    })
  })
  .enter().append("path")
  .attr("fill", function(d, i) {
    return color(i);
  })
  .attr("d", function(d, i, j) {
    return arc.innerRadius(10 + cwidth * d.parentIndex).outerRadius(cwidth * (d.parentIndex + 1))(d.payload);
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>


Solution

  • var dataset = [{
      "name": "Population Quater",
      "code": "POP_QUATER",
      "parent": "POP_BY_QUAT",
      "children": [{
        "name": "POP_CYQ1",
        "code": "POP_CYQ1",
        "parent": "POP_QUATER",
        "value": "6772",
        "label": "CYQ1",
        "children": []
      }, {
        "name": "POP_CYQ2",
        "code": "POP_CYQ2",
        "parent": "POP_QUATER",
        "value": "6716",
        "label": "CYQ2",
        "children": []
      }, {
        "name": "POP_CYQ3",
        "code": "POP_CYQ3",
        "parent": "POP_QUATER",
        "value": "6714",
        "label": "CYQ3",
        "children": []
      }, {
        "name": "POP_CYQ4",
        "code": "POP_CYQ4",
        "parent": "POP_QUATER",
        "value": "6703",
        "label": "CYQ4",
        "children": []
      }, {
        "name": "POP_LYQ1",
        "code": "POP_LYQ1",
        "parent": "POP_QUATER",
        "value": "6721",
        "label": "LYQ1",
        "children": []
      }, {
        "name": "POP_LYQ2",
        "code": "POP_LYQ2",
        "parent": "POP_QUATER",
        "value": "6671",
        "label": "LYQ2",
        "children": []
      }, {
        "name": "POP_LYQ3",
        "code": "POP_LYQ3",
        "parent": "POP_QUATER",
        "value": "6708",
        "label": "LYQ3",
        "children": []
      }, {
        "name": "POP_LYQ4",
        "code": "POP_LYQ4",
        "parent": "POP_QUATER",
        "value": "6734",
        "label": "LYQ4",
        "children": []
      }]
    }, {
      "name": "Transient Pop",
      "code": "TRANSIENT_POP",
      "parent": "POP_BY_QUAT",
      "label": "Transient Pop",
      "children": [{
        "name": "TRANSIENT_LYQ1",
        "code": "TRANSIENT_LYQ1",
        "parent": "TRANSIENT_POP",
        "value": "54",
        "label": "LYQ1",
        "children": []
      }, {
        "name": "TRANSIENT_LYQ2",
        "code": "TRANSIENT_LYQ2",
        "parent": "TRANSIENT_POP",
        "value": "86",
        "label": "LYQ2",
        "children": []
      }, {
        "name": "TRANSIENT_LYQ3",
        "code": "TRANSIENT_LYQ3",
        "parent": "TRANSIENT_POP",
        "value": "219",
        "label": "LYQ3",
        "children": []
      }, {
        "name": "TRANSIENT_LYQ4",
        "code": "TRANSIENT_LYQ4",
        "parent": "TRANSIENT_POP",
        "value": "191",
        "label": "LYQ4",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ1",
        "code": "TRANSIENT_CYQ1",
        "parent": "TRANSIENT_POP",
        "value": "52",
        "label": "CYQ1",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ2",
        "code": "TRANSIENT_CYQ2",
        "parent": "TRANSIENT_POP",
        "value": "91",
        "label": "CYQ2",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ3",
        "code": "TRANSIENT_CYQ3",
        "parent": "TRANSIENT_POP",
        "value": "222",
        "label": "CYQ3",
        "children": []
      }, {
        "name": "TRANSIENT_CYQ4",
        "code": "TRANSIENT_CYQ4",
        "parent": "TRANSIENT_POP",
        "value": "186",
        "label": "CYQ4",
        "children": []
      }]
    }, {
      "name": "Seasonal Pop",
      "code": "SEASONAL_POP",
      "parent": "POP_BY_QUAT",
      "label": "Seasonal Pop",
      "children": [{
        "name": "SEASONAL_LYQ1",
        "code": "SEASONAL_LYQ1",
        "parent": "SEASONAL_POP",
        "value": "2",
        "label": "LYQ1",
        "children": []
      }, {
        "name": "SEASONAL_LYQ2",
        "code": "SEASONAL_LYQ2",
        "parent": "SEASONAL_POP",
        "value": "24",
        "label": "LYQ2",
        "children": []
      }, {
        "name": "SEASONAL_LYQ3",
        "code": "SEASONAL_LYQ3",
        "parent": "SEASONAL_POP",
        "value": "152",
        "label": "LYQ3",
        "children": []
      }, {
        "name": "SEASONAL_LYQ4",
        "code": "SEASONAL_LYQ4",
        "parent": "SEASONAL_POP",
        "value": "55",
        "label": "LYQ4",
        "children": []
      }, {
        "name": "SEASONAL_CYQ1",
        "code": "SEASONAL_CYQ1",
        "parent": "SEASONAL_POP",
        "value": "2",
        "label": "CYQ1",
        "children": []
      }, {
        "name": "SEASONAL_CYQ2",
        "code": "SEASONAL_CYQ2",
        "parent": "SEASONAL_POP",
        "value": "22",
        "label": "CYQ2",
        "children": []
      }, {
        "name": "SEASONAL_CYQ3",
        "code": "SEASONAL_CYQ3",
        "parent": "SEASONAL_POP",
        "value": "161",
        "label": "CYQ3",
        "children": []
      }, {
        "name": "SEASONAL_CYQ4",
        "code": "SEASONAL_CYQ4",
        "parent": "SEASONAL_POP",
        "value": "55",
        "label": "CYQ4",
        "children": []
      }]
    }];
    
    var width = 460,
      height = 300,
      cwidth = 25;
    
    
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    
    var pie = d3.pie()
      .sort(null).value(function(d) {
        return d.value; //since score is the parameter for the pie
      });
    
    var arc = d3.arc();
    
    var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
    
    var gs = svg.selectAll("g")
      .data(dataset)
      .enter()
      .append("g")
      .attr("class", "arc").on("mouseover", function() {
        tooltip.style("display", null);
      });
    
    var tooltip = d3.select("body").append("div")
      .attr("class", "tooltip")
      .style("opacity", 0.5);
    
    tooltip.append("rect")
      .attr("width", 30)
      .attr("height", 20)
      .attr("fill", "#ffffff")
      .style("opacity", 0.5);
    
    tooltip.append("div")
      .attr("x", 15)
      .attr("dy", "1.2em")
      .style("text-anchor", "middle")
      .attr("font-size", "1.5em")
      .attr("font-weight", "bold");
    
    
    var path = gs.selectAll("path")
      .data(function(d, i) {
        return pie(d.children).map(function(payload) {
          return {
            payload: payload,
            parentIndex: i
          }
        })
      })
      .enter().append("path")
      .attr("fill", function(d, i) {
        return color(i);
      })
      .attr("d", function(d, i, j) {
        return arc.innerRadius(10 + cwidth * d.parentIndex).outerRadius(cwidth * (d.parentIndex + 1))(d.payload);
      })
      .on("mouseout", function() {
        tooltip.style("display", "none");
      })
    .on("mousemove", function(d, i) {
      console.log(d)
      tooltip.transition().duration(200)
        .style("opacity", 0.9);
      tooltip.select("div").html(d.payload.data.name + ":" + " <strong>" + d.payload.data.value + "</strong>")
        .style("position", "fixed")
        .style("text-align", "center")
        .style("width", "120px")
        .style("height", "45px")
        .style("padding", "2px")
        .style("font", "12px sans-serif")
        .style("background", "lightsteelblue")
        .style("border", "0px")
        .style("border-radius", "8px")
        .style("left", (d3.event.pageX) + "px")
        .style("top", (d3.event.pageY - 28) + "px");
    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>

    The event handlers were bound to the g tags instead of the path tags :) try this