d3 tooltip | passing in a variable

烈酒焚心 提交于 2020-01-07 02:47:08

问题


I have a webpage with 4 d3 charts for each of 11 different regions. One of those charts is an area chart and the code snippet is:

for (mm=0; mm<regions.length; mm++) {

    areas.append("path")
        .attr("class",function(d,i){ return "area cons ar"+i+" region"+mm;} )
        .attr("id", function(d,i) { return "area_"+i+"_"+mm})
        .attr("d", function(d,i) { return area(d)} );

    var test = d3.selectAll("path.region"+mm)
    .call(d3.helper.tooltip()
        .attr("class", function(d, i) { return "tooltip"; })
        .text(function(d, i){ 

            console.log(mm);

            return "i "+consSubProducts[i]+', i: '+i;}));
}

I want to add tooltips to the charts. In the area plot, each region has different products. Some have 7 products, others have 5. I need to use the mm variable at its runtime values (0-10) to call the correct product array where consSubProducts is currently. (ConsSubProducts is set to a different product array at the top of the for...loop, but as with mm, the code can only see the finally-set array and not the runtime arrays.)

In this code, mm always returns 11, i.e. it returns the final value of mm rather than the values at runtime.

I have tried passing mm in within tooltip() and within .text(function(d,i,mm) - the latter clearly doesn't work as it's expecting a j. I've also tried attaching mm to the class or ID of an object, but within the call() console.log(this) logs object.window.

I've tried modifying tooltip.js but although I can generate the label I want I can't work out how to override the text. Tooltip.js:

d3.helper = {};

d3.helper.tooltip = function(){
    var tooltipDiv;
    var bodyNode = d3.select('body').node();
    var attrs = {};
    var text = '';
    var styles = {};

    function tooltip(selection){

        selection.on('mouseover.tooltip', function(pD, pI){
            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).length > 80) ? '300px' : null; })
                .html(function(d, i){return text(pD, pI);});
        })
        .on('mousemove.tooltip', function(pD, pI){
            // 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); });
        })
        .on('mouseout.tooltip', function(pD, pI){
            // Remove tooltip
            tooltipDiv.remove();
        });

    }

    tooltip.attr = function(_x){
        if (!arguments.length) return attrs;
        attrs = _x;
        return this;
    };

    tooltip.style = function(_x){
        if (!arguments.length) return styles;
        styles = _x;
        return this;
    };

    tooltip.text = function(_x){
        if (!arguments.length) return text;
        text = d3.functor(_x);
        return this;
    };

    return tooltip;
};

Any help appreciated!


回答1:


It looks like this is way over the top for something that is super simple. Unless I'm missing something. Let me post some code for how I handle tooltips. Predefine the tooltip div in the page HTML and class it .tooltip and apply the css of display:none; position:absolute; and add .tooltip.show{display:block;}

var tooltip = d3.select('.tooltip');
function tipUpdate(d,i) {
 tooltip.classed('show',true).html(d+" : "+i);
}

areas.append('path')
 //all your path attributs
 .on('mouseover', tipUpdate)
 .on('mouseout', function() { tooltip.classed('show',false); });

areas.on('mousemove', function(d,i) {
  var mouse = d3.mouse('body').map( function(d) { return parseInt(d); });
  tooltip.attr("style", "left:"+(mouse[0]+10)+"px;top:"+(mouse[1]-15)+"px");
});

This will show the tooltip div on mouseover of the path and move the tooltip relative to it's position in the body and hide the tooltip when the mouse leaves the path. It will display the data and index of the point in the path that it is currently over. You might want to change that but it should be easy.

One problem I thought about later is you cannot do a working tooltip like this on a path. It will always show the data from the last point in the path. I have posted a working fiddle. D3 tooltip



来源:https://stackoverflow.com/questions/34659444/d3-tooltip-passing-in-a-variable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!