Skip to content

Instantly share code, notes, and snippets.

@truher
Last active December 12, 2015 05:18
Show Gist options
  • Save truher/4720183 to your computer and use it in GitHub Desktop.
Save truher/4720183 to your computer and use it in GitHub Desktop.
d3 tooltips
/*
* selection datum should contain a 'label' field, which can
* contain multiple lines. first line is emphasized.
*
* selection datum should accomodate the tips fifo called 'tips'.
*
* using a single tooltip element exposes the add/remove race,
* so it's easier to have lots of them; by id. that way we can
* nicely transition the fade of the old one, while creating
* new ones. so each mouseover target datum gets a fifo of
* tooltips called 'tips'.
*
* container is where to append the tooltip. this should
* be an outer container of the 'tipped' items, so the tip itself
* is painted last.
*/
this.tooltip = function(container) {
var ttidx = 0;
var my = function(sel) {
sel.each(function(datum) {
// each selection is a mouseover target
var selection = d3.select(this);
selection
.on("mouseover", function(){
ttidx += 1;
var tooltip = container.selectAll('g#tip'+ttidx).data([datum]);
if (_.isUndefined(datum.tips)) {
datum.tips = [];
}
datum.tips.push(ttidx);
tooltip.enter().append('g');
tooltip.exit().remove();
// start out invisible; we move this box below and then expose it.
tooltip
.attr('class','tooltip2')
.attr('id','tip'+ttidx) // unique id!
.attr('opacity', 0);
// rect is underneath, so goes first
var ttrect = tooltip.selectAll('rect').data([datum]);
ttrect.enter().append('rect');
// text container
var tttext = tooltip.selectAll('text').data([datum]);
tttext.enter().append('text');
tttext.attr('x', 0).attr('y', 0).attr('text-anchor','start');
// split lines into tspans
var ttspan = tttext.selectAll('tspan').data(function(d2){
return d2.label.split(/\r\n|\r|\n/g);
});
ttspan.enter().append('tspan');
ttspan
.attr('x',0)
.attr('dy',function(d1,i){
return (i===0?'1em':(i===1)?'1.5em':'1em');
})
.attr('class',function(d1,i){return (i===0?'head':'');})
ttspan.text(function(dspan){ return dspan; });
var bbox = tttext.node().getBBox();
var padding = 10;
ttrect
.attr('width', bbox.width + 2 * padding)
.attr('height', bbox.height + 2 * padding)
.attr('x',0 - padding)
.attr('y',0 - padding)
.attr('rx',5)
.attr('ry',5);
var xy = d3.mouse(this);
var xoffset = 20;
var yoffset = 20;
// if there's room on the right, put the box there.
var right = width - (xy[0] + xoffset + bbox.width + padding);
// if there's room on the bottom, put the box there.
var bottom = height - (xy[1] + yoffset + bbox.height + padding);
var xt;
if (right > 0) {xt = xy[0] + xoffset;}
else {xt = xy[0] - (xoffset + bbox.width);}
var yt;
if (bottom > 0) {yt = xy[1] + yoffset;}
else {yt = xy[1] - (yoffset + bbox.height);}
tooltip
.attr('transform', 'translate(' + xt + ',' + yt + ')')
.attr('opacity', 1);
})
.on("mouseout", function(){
var rmidx = datum.tips.shift();
var tooltip = container.selectAll('g#tip'+rmidx);
tooltip.transition(2000).attr('opacity', 0).remove(); // fade out slowly
});
});
};
var width = 0;
var height = 0;
// paint the tip inside this width
my.width = function(v) {
if (!arguments.length) return width;
width = v;
return my;
};
// paint the tip inside this height
my.height = function(v) {
if (!arguments.length) return height;
height = v;
return my;
};
return my;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment