0

After 6 long hours, I managed to add just a couple of more lines to my example, following my previous post (D3 tooltip show values from nested dataset) concerning the use of tooltips.

Now I am stuck at a different point - I can't make circle points that snap to the line points. Other users have already pointed me to a few directions (thanks @Mark), but still haven't been able to combine everything and make it work as I want it.

I have created one circle for each line with its corresponding line color. When hovering over with the mouse, a tooltip with all the lines' values appears and the circles must be positioned on the lines on the x and y axis.

My problem lies in the following piece of code, located inside the mousemove function, in line #106 of this fiddle edit: updated fiddle (https://jsfiddle.net/2en21Lqh/13/):

d3.selectAll(parent + ' .d3-focuspoint')
  .classed("hidden", false)
  .attr("cx", xz(lastDate))
  .attr("cy", function(c) { 
    return d3.map(c.values, function(d,i) {
      if (lastDate == d.date) {
        console.log(d.value);
        return d.value;
      }
   })
});

The circles are already bound to the existing data (two days ago I wouldn't have figured this on my own! - at least I am sligthly improving! :/ ), so I am trying to apply the correct cy value but can't figure out the way to do it. The function inside cy returns an object rather than the d.value I am looking for. What am I doing wrong? I've been trying for hours to find any similar examples but can't find any :/

edit: even pointers to the right direction would help :/

Community
  • 1
  • 1
scooterlord
  • 15,124
  • 11
  • 49
  • 68

1 Answers1

1

Try this:

var mousemoveFunc = function(d, i) {

  var x0 = xz.invert(d3.mouse(this)[0]);

  var lastDate,
      cys = [], //<-- create array to hold y values for circles
      ds = []; //<-- create array to hold tooltip text

  dataGroup.forEach(function(e) { //<-- loop the data (I removed the map since we now need to return two things)
    var i = bisectDate(e.values, x0, 1),
        d0 = e.values[i - 1],
        d1 = e.values[i];

    var d = x0 - d0.date > d1.date - x0 ? d1 : d0;

    lastDate = d.date; //<-- hold onto the date (same for all xs)
    cys.push(d.value); //<-- hold onto the y value for all circles
    ds.push(e.key + " " + d.value); //<-- make the tooltip line
  });

  var mouse = d3.mouse(svg.node()).map(function(d) {
    return parseInt(d);
  });
  var left = Math.min(containerwidth, mouse[0]+margin.left+margin.right),
      top = Math.min(containerheight, mouse[1]+margin.top+margin.right);

  d3.selectAll(parent + ' .d3-focuspoint')
    .classed("hidden", false)
    .attr("cx", xz(lastDate)) //<-- set x position
    .attr("cy", function(d,i) {        
        return yz(cys[i]); //<-- loop the 3 circles and set y position
  });

  tooltip
    .html(lastDate.toString() + "<br/>" + ds.join("<br/>"))
    .classed('hidden', false)
    .style('left', left + 'px')
    .style('top', top + 'px');          

};

Updated fiddle.

Mark
  • 106,305
  • 20
  • 172
  • 230
  • my saviour.. as always...can you elaborate on what you did? I am not interested into just getting an answer, but what's happening. – scooterlord Mar 25 '17 at 13:33
  • 1
    @scooterlord, updated my code snippet above with lots of comments for explanation. – Mark Mar 25 '17 at 13:45
  • if you notice in the fiddle, if you remove lines one by one, then the cys object isn't bound correctly. Points appear for different lines - that was the initial issue I had and was trying to follow the path in my original question. – scooterlord Mar 25 '17 at 13:51
  • 1
    @scooterlord, I got you now, the line doesn't exist but it's drawing circles for them anyway, https://jsfiddle.net/2en21Lqh/17/ which just adds `if (e.enabled) cys.push(d.value); if (e.enabled) ds.push(e.key + " " + d.value);` into the mousemove. – Mark Mar 25 '17 at 14:05
  • Thanks again! Looks like I have a lot to read during the weekend... :/ – scooterlord Mar 25 '17 at 14:10
  • Last question (hopefully you are still enjoying them!). When we do dataGroup.forEach, the new arrays are bound correctly to each item, in index order? – scooterlord Mar 25 '17 at 14:18
  • 1
    @scooterlord, `forEach` doesn't have anything to do with data-binding. In fact, your circles were never data bound at all (we never called `.data` or `.datum` on their selection). `forEach` is pure JavaScript, it just iterates the array `dataGroup`. – Mark Mar 25 '17 at 17:10
  • Don't the circles inherit (sort of speak) data binding from their parent elements? When we use .data on an element and append to it doesn't the child element carry any information from the parent? Calling the forEach function produces an array we use to match colors; does this occur because the data is always processed by increasing index? – scooterlord Mar 25 '17 at 19:19
  • 1
    @scooterlord, `on't the circles inherit (sort of speak) data binding from their parent elements?` absolutely they do! But the way the data-binding is set-up they hold the data for the whole line (inherited from the `g`). `does this occur because the data is always processed by increasing index`, the circles are just indexed in the same order as the data. – Mark Mar 25 '17 at 19:48