Javascript – Adding tooltip in d3.js map

d3.js, javascript, tooltip, topojson

I'm trying to add a tooltip showing the name of districts when you hover over it in the map in d3.js. The input is a topojson file and I've been able to successfully generate the map with district boundaries and highlight the currently selected district.

For the tooltip I tried doing something similar to this, but nothing happens at all. The code I've used is given below. The tooltip code is towards the end.

var width = 960,    height = 600;var projection = d3.geo.albers()    .center([87, 28])    .rotate([-85, 0])    .parallels([27, 32]);var path = d3.geo.path()    .projection(projection);var svg = d3.select("body").append("svg")    .attr("width", width)    .attr("height", height);svg.append("rect")    .attr("width", width)    .attr("height", height);  var g = svg.append("g");  var div = d3.select("body").append("div")  .attr("class", "tooltip")  .style("opacity", 1e-6);d3.json("data/nepal3.json", function(error, npl) {    var districts = topojson.feature(npl, npl.objects.nepal_districts);    projection      .scale(1)      .translate([0, 0]);    var b = path.bounds(districts),      s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),      t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];    projection      .scale(s)      .translate(t);      g.selectAll(".nepal_districts")          .data(districts.features)        .enter().append("path")          .attr("class", function(d) { return "nepal_districts " + d.id; })          .attr("d", path)          .on("mouseover", function(d,i) {              d3.select(this.parentNode.appendChild(this)).transition().duration(300)                  .style({'stroke-width':2,'stroke':'#333333','stroke-linejoin':'round','cursor':'pointer','fill':'#b9270b'});          })          .on("mouseout", function(d,i) {              d3.select(this.parentNode.appendChild(this)).transition().duration(100)                  .style({'stroke-width':2,'stroke':'#FFFFFF','stroke-linejoin':'round','fill':'#3d71b6'});          });      g.append("path")          .datum(topojson.mesh(npl, npl.objects.nepal_districts, function(a, b) { return a !== b;}))          .attr("d", path)          .attr("class", "district-boundary");      /* Tooltip */      g.selectAll(".nepal_districts")          .data(districts.features)        .enter().append("text")          .append("svg:rect")            .attr("width", 140)            .attr("height", 140)            .text(function(d) { return d.properties.name; })            .on("mouseover", mouseover)            .on("mousemove", mousemove)            .on("mouseout", mouseout);      function mouseover() {        div.transition()            .duration(300)            .style("opacity", 1);      }      function mousemove() {        div            .text(d3.event.pageX + ", " + d3.event.pageY)            .style("left", (d3.event.pageX - 34) + "px")            .style("top", (d3.event.pageY - 12) + "px");      }      function mouseout() {        div.transition()            .duration(100)            .style("opacity", 1e-6);      } });

The CSS is

div.tooltip {     position: absolute;             text-align: center;             width: 60px;                    height: 28px;                   padding: 2px;               font: 12px sans-serif;          background: #4c4c4c;     border: 0px;        border-radius: 8px;             pointer-events: none;         }

The code I added for "Tooltip" does nothing at all. What am I doing wrong here?

The topojson file has this format. I wanted to get the "name" property to show up in the Tooltip.

{  "type": "Topology",  "objects": {    "nepal_districts": {      "type": "GeometryCollection",      "geometries": [        {          "type": "Polygon",          "id": 0,          "properties": {            "name": "HUMLA"          },          "arcs": [            [              0,              1,              2,              3            ]          ]        },

Best Solution

Had a similar problem where I ended up adding absolute positioned tooltip to body element, and modyfying its placement according to mouse position.

Add to directive:

function addTooltip(accessor) {    return function(selection) {        var tooltipDiv;        var bodyNode = d3.select('body').node();        selection.on("mouseover", function(topoData, countryIndex) {            if (!accessor(topoData, countryIndex)) {                return;            }            // Clean up lost tooltips            d3.select('body').selectAll('div.tooltipmap').remove();            formatValue(topoData, countryIndex);            tooltipDiv = d3.select('body').append('div').attr('class', 'tooltipmap');            var absoluteMousePos = d3.mouse(bodyNode);            tooltipDiv.style('left', (absoluteMousePos[0] + 10) + 'px')                .style('top', (absoluteMousePos[1] - 15) + 'px')                .style('opacity', 1)                .style('z-index', 1070);            accessor(topoData, countryIndex) || '';        })            .on('mousemove', function(topoData, countryIndex) {                if (!accessor(topoData, countryIndex)) {                    return;                }                var absoluteMousePos = d3.mouse(bodyNode);                tooltipDiv.style('left', (absoluteMousePos[0] + 10) + 'px')                    .style('top', (absoluteMousePos[1] - 15) + 'px');                var tooltipText = accessor(topoData, countryIndex) || '';                tooltipDiv.html(tooltipText);            })            .on("mouseout", function(topoData, countryIndex) {                if (!accessor(topoData, countryIndex)) {                    return;                }                tooltipDiv.remove();            });    };.tooltipmap{   background-color: #000000;  margin: 10px;  height: 50px;  width: 150px;  padding-left: 10px;   padding-top: 10px;  border-radius: 5px;  overflow: hidden;  display: block;  color: #FFFFFF;  font-size: 12px;  position: absolute;  opacity: 1;  h6{    margin: 0;    padding: 0;  }  p{    color: #FFFFFF;  }}

Hope it helps!