mmuniz
mmuniz

Reputation: 145

Fix d3.js v4 tooltip position

I'm been trying to display two charts in my web page using the d3 v4 plugin

I have been following some examples about tooltip's usage but I have a problem: In the first chart the tooltips are displayed in the correct position (above the bars) but in the bottom chart, the tooltips seems to going to the bottom of the page.

This is a very simple example: https://jsfiddle.net/xvs98vru/

HTML

<div class="container">
    <div class="row">
        <div class="col-md-12">
            <div id="chart1" class="c"></div>
        </div>
        <div class="col-md-12">
            <div id="chart2" class="c"></div>
        </div>
    </div>
</div>

CSS

body { padding: 20px; }

.c {
  height: 300px;
  width: 400px;
  margin-bottom: 10px;
}

.tooltipx {
    pointer-events: none;
    position: absolute;
    display: none;
    min-width: 50px;
    height: auto;
    background: none repeat scroll 0 0 #ffffff;
    padding: 4px 10px 4px 10px;
    border-radius: 4px;
    text-align: left;
    line-height: 1.3;
    color: #5B6770;
    box-shadow: 0px 3px 9px rgba(0, 0, 0, .15);
}

JS

(function() {
    var mrg = { t:10, r:20, b:30, l:20 };

    var data = [
        { id: 1, amount: 6 },
        { id: 2, amount: 5 },
        { id: 3, amount: 2 },
        { id: 4, amount: 8 }
    ];

    function loadChart(container) {
        var width = 400 - mrg.l - mrg.r;
        var height = 300 - mrg.t - mrg.b;

        var color = d3.scaleOrdinal(d3.schemeCategory10);
        var tooltip = d3.select(container).append("div")
                        .attr("class", "tooltipx")
                        .style("opacity", 0);

        var x = d3.scaleBand().range([0, width]).padding(0.1);
        var y = d3.scaleLinear().range([height, 0]);

        x.domain([1, 2, 3, 4]);
        y.domain([0, 10]);

        var svg = d3.select(container).append("svg")
                    .attr("width", width + mrg.l + mrg.r)
                    .attr("height", height + mrg.t + mrg.b)
                    .append("g")
                    .attr("transform", "translate(" + mrg.l + "," + mrg.t + ")");

        svg.selectAll(".bar")
           .data(data)
           .enter()
           .append("rect")
           .attr("class", "bar")
           .attr("x", function(n) { return x(n.id); })
           .attr("y", function(n) { return y(n.amount); })
           .attr("width", x.bandwidth())
           .attr("height", function(n) { return height - y(n.amount); })
           .attr("fill", function(n, i) { return color(i); })
           .on("mousemove", function(n) {
               tooltip
                   .style("opacity", 1)
                   .style("left", d3.event.pageX + "px")
                   .style("top", d3.event.pageY + "px")
                   .style("display", "inline-block")
                   .html("<span>Hola</span>")
           })
           .on("mouseout", function(n) { tooltip.style("opacity", 0); });

        svg.append("g")
           .attr("transform", "translate(0," + height + ")")
           .call(d3.axisBottom(x));

        svg.append("g").call(d3.axisLeft(y));
    }

    function init() {
        loadChart("#chart1");
        loadChart("#chart2");
    }

    init();

}());

Thank's for the support

Regards

Upvotes: 2

Views: 3113

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102174

Since you're using pageX and pageY for positioning the tooltips, don't append them to each selected div... instead of that, append them to the <body>:

var tooltip = d3.select("body").append("div")

Here is your code with that change only:

(function() {
  var mrg = {
    t: 10,
    r: 20,
    b: 30,
    l: 20
  };

  var data = [{
      id: 1,
      amount: 6
    },
    {
      id: 2,
      amount: 5
    },
    {
      id: 3,
      amount: 2
    },
    {
      id: 4,
      amount: 8
    }
  ];

  function loadChart(container) {
    var width = 400 - mrg.l - mrg.r;
    var height = 300 - mrg.t - mrg.b;

    var color = d3.scaleOrdinal(d3.schemeCategory10);
    var tooltip = d3.select("body").append("div")
      .attr("class", "tooltipx")
      .style("opacity", 0);

    var x = d3.scaleBand().range([0, width]).padding(0.1);
    var y = d3.scaleLinear().range([height, 0]);

    x.domain([1, 2, 3, 4]);
    y.domain([0, 10]);

    var svg = d3.select(container).append("svg")
      .attr("width", width + mrg.l + mrg.r)
      .attr("height", height + mrg.t + mrg.b)
      .append("g")
      .attr("transform", "translate(" + mrg.l + "," + mrg.t + ")");

    svg.selectAll(".bar")
      .data(data)
      .enter()
      .append("rect")
      .attr("class", "bar")
      .attr("x", function(n) {
        return x(n.id);
      })
      .attr("y", function(n) {
        return y(n.amount);
      })
      .attr("width", x.bandwidth())
      .attr("height", function(n) {
        return height - y(n.amount);
      })
      .attr("fill", function(n, i) {
        return color(i);
      })
      .on("mousemove", function(n) {
        tooltip
          .style("opacity", 1)
          .style("left", d3.event.pageX + "px")
          .style("top", d3.event.pageY + "px")
          .style("display", "inline-block")
          .html("<span>Hola</span>")
      })
      .on("mouseout", function(n) {
        tooltip.style("opacity", 0);
      });

    svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));
    svg.append("g").call(d3.axisLeft(y));
  }

  function init() {
    loadChart("#chart1");
    loadChart("#chart2");
  }

  init();

}());
body {
  padding: 20px;
}

.c {
  height: 300px;
  width: 400px;
  margin-bottom: 10px;
}

.tooltipx {
  pointer-events: none;
  position: absolute;
  display: none;
  min-width: 50px;
  height: auto;
  background: none repeat scroll 0 0 #ffffff;
  padding: 4px 10px 4px 10px;
  border-radius: 4px;
  text-align: left;
  line-height: 1.3;
  color: #5B6770;
  box-shadow: 0px 3px 9px rgba(0, 0, 0, .15);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div class="container">
  <div class="row">
    <div class="col-md-12">
      <div id="chart1" class="c"></div>
    </div>
    <div class="col-md-12">
      <div id="chart2" class="c"></div>
    </div>
  </div>
</div>

Upvotes: 4

Related Questions