FrontEnd Expert
FrontEnd Expert

Reputation: 5813

Tooltip is not showing at appended 'rect' on top of point/circle in d3 chart

I have a d3 line chart with a tooltip, I am facing a problem with a tooltip.

I have functionality, on click of points/circle I am appending rect to g, which is adding on top of the existing rect which has the tooltip functionality.

My tooltip is not coming at selected(rect) Graph Point.

g.append("rect")
      .attr("class", "overlay")
      .attr("id", "firstLayer")
      .attr("width", width)
      .attr("height", height)

      .on("mouseover", function(d) {
        focus.style("display", null);
        div
          .transition()
          .duration(200)
          .style("opacity", 0.9);
      })

      .on("click", function(d, index) {
        let newXScale = newX ? newX : xScale;
        if (rect) rect.remove();
        rect = g
          .append("rect")
          .attr("x", newXScale(d.startTime) - 12.5)
          .attr("y", 0)
          .attr("width", 24)
          .attr("height", height + 5)
          .attr("data", d.startTime)
          .style("fill", "steelblue")
          .style("opacity", 0.5);

        if (clickLine) clickLine.remove();
        clickLine = g
          .append("line")
          .attr("x1", newXScale(d.startTime))
          .attr("y1", yScale(yDomain[0]))
          .attr("x2", newXScale(d.startTime))
          .attr("y2", yScale(yDomain[1]))
          .attr("class", "focusLine")
          .style("opacity", 0.5);
      })

rect element is coming on top of the gm on hover of that tooltip is not coming, any suggestions on how to fix it ?

On mouse hover - enter image description here

At selected Graph Point -

enter image description here

CodeSandbox link below - https://codesandbox.io/s/damp-dawn-82hxc

Please guide me what can be changed.

Upvotes: 0

Views: 555

Answers (1)

JP Siyyadri
JP Siyyadri

Reputation: 111

on click of the circle you are appending another rectangle to g, which is adding on top of the existing rect which has the tool tip functionality

Note: d3 js adds layer/shape on top of another which basically overrides the existing layer/shape functionality with the new layer/shape if they are in the same position

To avoid that we have to draw the layers depends on their intended purpose and position.

Solution for the above problem

append background rects for circle you want to create with opacity: 0

g.selectAll(".faaa")
  .data(data)
  .enter()
  .append("rect")
  .attr("class", "faaa")
  .attr("id", d => "rect_" + d.id)
  .attr("x", d => xScale(d.startTime) - 12.5)
  .attr("y", 0)
  .attr("width", 24)
  .attr("height", height + 5)
  .attr("data", d => d)
  .style("fill", "steelblue")
  .style("opacity", 0);

append firstLayer rect which has the tooltip functionality so the background rect won't break the tooltip functionality

g.append("rect")
  .attr("class", "overlay")
  .attr("id", "firstLayer")
  .attr("width", width)
  .attr("height", height)

  .on("mouseover", function(d) {
    focus.style("display", null);
    div
      .transition()
      .duration(200)
      .style("opacity", 0.9);
  })
  .on("mouseout", function() {
    focus.style("display", "none");
    div
      .transition()
      .duration(300)
      .style("opacity", 0);
  })

  .on("mousemove", function() {
    var mouse = d3.mouse(this);
    var mouseDate = xScale.invert(mouse[0]);
    var i = bisectDate(data, mouseDate); // returns the index to the current data item

    var d0 = data[i - 1];
    var d1 = data[i];
    let d;
    // work out which date value is closest to the mouse
    if (typeof d1 !== "undefined") {
      d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
    } else {
      d = d0;
    }

    div
      .html(
        `<span>${parseDate(d.startTime)}</span>
        <span> Changes : ${d.magnitude} % </span>`
      )
      .style("left", d3.event.pageX + "px")
      .style("top", d3.event.pageY - 28 + "px");
    var x = xScale(d.startTime);
    var y = yScale(d.magnitude);

    focus
      .select("#focusCircle")
      .attr("cx", x)
      .attr("cy", y);
    focus
      .select("#focusLineX")
      .attr("x1", x)
      .attr("y1", yScale(yDomain[0]))
      .attr("x2", x)
      .attr("y2", yScale(yDomain[1]));
    focus
      .select("#focusLineY")
      .attr("x1", xScale(xDomain[0]))
      .attr("y1", y)
      .attr("x2", xScale(xDomain[1]))
      .attr("y2", y);
  });

append circle and add click functionality then change the opacity to highlight the background rect

  g.selectAll(".foo")
    .data(data)
    .enter()
    .append("circle")
    .attr("id", d => d.id)
    .attr("class", "foo")
    .attr("data", d => d)
    .attr("cx", function(d) {
      return xScale(d.startTime);
    })
    .attr("cy", function(d) {
      return yScale(d.magnitude);
    })
    .attr("r", function(d) {
      return 6;
    })
    .on("click", function(d) {
      // change the opacity here
      d3.select("#rect_" + d.id).style("opacity", 0.5);
    })
    .attr("class", "circle");

Hope this solves the above problem...

Upvotes: 4

Related Questions