1

I am trying to add mouseover tooltip to multi-series line chart with D3 and Angular-cli. I followed this.

What I am trying is as below.

Define the lines

var line = d3.line()
  .x(function (d) {
    return x(d.date);
  })
  .y(function (d) {
    return y(d.lookbookcount);
  });

To add tooltip with a vertical line

// append a g for all the mouse over nonsense
var mouseG = svg.append("g")
  .attr("class", "mouse-over-effects");

// this is the vertical line
mouseG.append("path")
  .attr("class", "mouse-line")
  .style("stroke", "black")
  .style("stroke-width", "1px")
  .style("opacity", "0");

// keep a reference to all our lines
var lines = document.getElementsByClassName('line');
console.log(lines);

// here's a g for each circle and text on the line
var mousePerLine = mouseG.selectAll('.mouse-per-line')
  .data(lookBookData)
  .enter()
  .append("g")
  .attr("class", "mouse-per-line");

// the circle
mousePerLine.append("circle")
  .attr("r", 7)
  .style("stroke", function (d) {
    return z(d.name);
  })
  .style("fill", "none")
  .style("stroke-width", "1px")
  .style("opacity", "0");

// the text
mousePerLine.append("text")
  .attr("transform", "translate(10,3)");

// rect to capture mouse movements
mouseG.append('svg:rect')
  .attr('width', width)
  .attr('height', height)
  .attr('fill', 'none')
  .attr('pointer-events', 'all')
  .on('mouseout', function () { // on mouse out hide line, circles and text
    d3.select(".mouse-line")
      .style("opacity", "0");
    d3.selectAll(".mouse-per-line circle")
      .style("opacity", "0");
    d3.selectAll(".mouse-per-line text")
      .style("opacity", "0");
  })
  .on('mouseover', function () { // on mouse in show line, circles and text
    d3.select(".mouse-line")
      .style("opacity", "1");
    d3.selectAll(".mouse-per-line circle")
      .style("opacity", "1");
    d3.selectAll(".mouse-per-line text")
      .style("opacity", "1");
  })
  .on('mousemove', function () { // mouse moving over canvas
    var mouse = d3.mouse(this);

    // move the vertical line
    d3.select(".mouse-line")
      .attr("d", function () {
        var d = "M" + mouse[0] + "," + height;
        d += " " + mouse[0] + "," + 0;
        return d;
      });

    // position the circle and text
    d3.selectAll(".mouse-per-line")
      .attr("transform", function (d, i) {
        console.log(width / mouse[0])
        var xDate = x.invert(mouse[0]),
          bisect = d3.bisector(function (d) {
            return d.date;
          }).right,
          idx = bisect(d.values, xDate);

        // since we are use curve fitting we can't relay on finding the points like I had done in my last answer
        // this conducts a search using some SVG path functions
        // to find the correct position on the line
        // from http://bl.ocks.org/duopixel/3824661
        var beginning = 0,
          end = lines[i].getTotalLength(),
          target = null;

        while (true) {
          target = Math.floor((beginning + end) / 2);
          var pos = lines[i].getPointAtLength(target);
          if ((target === end || target === beginning) && pos.x !== mouse[0]) {
            break;
          }
          if (pos.x > mouse[0])      end = target;
          else if (pos.x < mouse[0]) beginning = target;
          else break; //position found
        }

        // update the text with y value
        d3.select(this).select('text')
          .text(y.invert(pos.y).toFixed(2));

        // return position
        return "translate(" + mouse[0] + "," + pos.y + ")";
      });
  });

loohBookData is defines as below.

var lookBookData = z.domain().map(function (name) {
  return {
    name: name,
    values: data.map(function (d) {
      return {date: d.date, lookbookcount: d[name], name: name};
    })
  };
});

var chartdata = svg.selectAll(".chartdata")
  .data(lookBookData)
  .enter().append("g")
  .attr("class", "chartdata");

chartdata.append("path")
  .attr("class", "line")
  .attr("d", function (d) {
    return line(d.values);
  })
  .style("fill", "none")
  .style("stroke", function (d) {
    return z(d.name);
  })
  .style("stroke-width", "2px");

chartdata.append("text")
  .datum(function (d) {
    return {
      name: d.name, value: d.values[d.values.length - 1]
    };
  })
  .attr("transform", function (d) {
    return "translate(" +
      x(d.value.date) + "," + y(d.value.lookbookcount) + ")";
  })
  .attr("x", 3)
  .attr("dy", "0.35em")
  .style("font", "14px open-sans")
  .text(function (d) {
    return d.name;
  });

Following console log

console.log(lines);

prints path array as below.

[path, path]

lines[0] prints

<path class=​"line" d=​"M0,112.5L83,58.928571428571445L166,32.14285714285711L249,166.07142857142856L332,171.42857142857144L415,0L498,107.14285714285717L581,112.5L664,5.357142857142833L747,112.5L830,112.5" style=​"fill:​ none;​ stroke:​ rgb(31, 119, 180)​;​ stroke-width:​ 2px;​">​</path>​

My IDE (Webstorm) says following build error.

Unresolved function or method getTotalLength()
Unresolved function or method getPointAtLength()

Any suggestions would be highly appreciated.

Thank You

Community
  • 1
  • 1
Rose18
  • 2,892
  • 8
  • 47
  • 98
  • It is most likely not helpful to post your full code. No one wants to sift through countless lines of code. Try boiling it down to a [mcve] which is merely sufficient to demonstrate the issue. Chances are, that during this process of condensing, you will find the solution yourself. – altocumulus Sep 01 '16 at 11:20
  • I have same problem :/ – bumbeishvili Aug 04 '17 at 17:19

0 Answers0