0

I'm trying to refer to the row reference j in D3 within a tooltip. I'm sure this is possible. I'm referring to j separately in an onChange event and it works great. But in the tooltip it comes back as undefined. Can anyone tell me why?

var test = svg.selectAll("g#container g.points")
    .data(categs)
.enter().append("g")
    .attr("class", function(d,i) {return "points pt"+i;})
    .style("stroke", function(d,i) {return "#"+z(i);})
    .attr("fill", function(d,i) {return "#"+z(i);})
.selectAll("circle")
    .data(function(d,i,j) {return d.values;})
    .enter().append("circle")
    .attr("cx", function(d,i) {return x(d.date);})
    .attr("cy", function(d,i) {return y(d.y+d.y0);})
    .attr("r", 5);

So there's one g#container, 3 g.points, and 5 circles per g.point. 'i' refers to 0-4 and i want j to refer to 0-2... The tooltip works great, popping up and containing i and d (not shown in the code below) but returns 'undefined' for j:

d3.selectAll("g#container g.points").selectAll("circle")
    .call(d3.helper.tooltip()
        .attr({class: "tooltip"})
        .text(function(d, i, j) { return "Category: "+j; })
);
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
emma
  • 784
  • 2
  • 9
  • 23

1 Answers1

0

This is a two-month old question, so I'm not sure if this can still help the original poster, but maybe it will help someone else:

When you pass a function as a parameter to another method, the parameter that are given to your function are determined by the code for that method. (Longer explanation here.) Most (although not all) d3 methods on selections will pass the values for the data object, the selection index, and the group index (d, i, and j in the common terminology). However, the group index is not well-established in examples and usage, and it isn't used at all by the code for the d3.helper.tooltip module you're using. The tooltip function never accesses j, and it never passes j to the functions you give it.

There are two ways you can fix this:

Option 1: Change the code for the tooltips, so that the event-handling functions accept the third parameter from the elements they are attached to, and pass that parameter on to the functions they call:

    /* from https://gist.github.com/biovisualize/2973775 */
    function tooltip(selection){

        selection.on('mouseover.tooltip', function(pD, pI, pJ){
            var name, value;
            // Clean up lost tooltips
            d3.select('body').selectAll('div.tooltip').remove();
            // Append tooltip
            tooltipDiv = d3.select('body').append('div');
            tooltipDiv.attr(attrs);
            tooltipDiv.style(styles);
            var absoluteMousePos = d3.mouse(bodyNode);
            tooltipDiv.style({
                left: (absoluteMousePos[0] + 10)+'px',
                top: (absoluteMousePos[1] - 15)+'px',
                position: 'absolute',
                'z-index': 1001
            });
            // Add text using the accessor function, Crop text arbitrarily
            tooltipDiv.style('width', function(d, i){ 
                     return (text(pD, pI, pJ).length > 80) ? '300px' : null; 
                })
                .html(function(d, i){return text(pD, pI, pJ);});
        })
        .on('mousemove.tooltip', function(pD, pI, pJ){
            // Move tooltip
            var absoluteMousePos = d3.mouse(bodyNode);
            tooltipDiv.style({
                left: (absoluteMousePos[0] + 10)+'px',
                top: (absoluteMousePos[1] - 15)+'px'
            });
            // Keep updating the text, it could change according to position
            tooltipDiv.html(function(d, i){ return text(pD, pI, pJ); });
        })
        .on('mouseout.tooltip', function(pD, pI, pJ){
            // Remove tooltip
            tooltipDiv.remove();
        });

    }

Option 2: If you don't want to change the module script, you could work around it by making sure that the index you're interested in is embedded within the data object for the element:

var test = svg.selectAll("g#container g.points")
    .data(categs)
.enter().append("g")
    .attr("class", function(d,i) {return "points pt"+i;})
    .style("stroke", function(d,i) {return "#"+z(i);})
    .attr("fill", function(d,i) {return "#"+z(i);})
.selectAll("circle")
    .data(function(groupData,groupIndex) {
              d.values.forEach(function(v){v.groupIndex = groupIndex;});
              //modify each value to reference the groupIndex
              //Note that in the data-join function the data and the *first*
              //index parameter refer to the data and index for the group
              return d.values;
              //each of the values will become a data object for a
              //separate circle in this group
    })
    .enter().append("circle")
    .attr("cx", function(d,i) {return x(d.date);})
    .attr("cy", function(d,i) {return y(d.y+d.y0);})
    .attr("r", 5)
    .call(d3.helper.tooltip()
        .attr({class: "tooltip"})
        .text(function(d, i) { return "Category: "+ d.groupIndex; })
            //use the saved index, passed to the tooltip function as part 
            //of the element's data object
    );
Community
  • 1
  • 1
AmeliaBR
  • 27,344
  • 6
  • 86
  • 119