Skip to content

Tooltip Code Example

Sophie Engle edited this page Apr 8, 2015 · 1 revision
/*
 * These are functions that are related to displaying tooltips.
 * They require the data and utility helper functions.
 */
function toolHelpers(util, data) {
    var main = {};

    var tooltip  = d3.select("span#tooltip");
    var maxWidth = 300;

    var drag = d3.behavior.drag().on("drag", function() {
        dragPosition(d3.select(this));
    });

    if (tooltip.empty()) {
        tooltip = d3.select("body").append("span");
        tooltip.attr("id", "tooltip");
        tooltip.attr("class", "blocktip");
        tooltip.style("visibility", "hidden");
        tooltip.style("position", "absolute");
    }

    main.hover = function(d) {
        d3.select(this).classed("highlight", true);

        if (!util.hasClass(d3.select(this), "sticky")) {
            updatePosition(tooltip, data.nodeLabel(d));
            tooltip.style("visibility", "visible");
        }
    }

    main.exit = function(d) {
        d3.select(this).classed("highlight", false);
        tooltip.style("visibility", "hidden");
    };

    main.sticky = function(d) {
        tooltip.style("visibility", "hidden");

        var selection = d3.select(this);

        if (util.hasClass(selection, "sticky")) {
            var id = selection.attr("id");

            selection.classed("sticky", false);
            selection.attr("id", null);

            d3.select("span#" + id).remove();
        }
        else {
            var sticky = d3.select("body").append("span");
            var sid = Date.now();

            sticky.attr("class", "blocktip");
            sticky.attr("id", "sticky-" + sid);
            sticky.style("position", "absolute");

            updatePosition(sticky, data.nodeLabel(d));

            sticky.on("dblclick", function() {
                var id = d3.select(this).attr("id");
                d3.select(this).remove();

                var cell = d3.select("#" + id);
                cell.classed("sticky", false);
                cell.attr("id", null);
            });

            sticky.call(drag);

            selection.classed("sticky", true);
            selection.attr("id", "sticky-" + sid);

            this.parentNode.appendChild(this);
        }
    };

    main.width = function(arg) {
        if (!arguments.length) return maxWidth;

        if (arg > 0) {
            maxWidth = arg;
        }

        return main;
    };

    // Hack required since cannot cause CSS to update width after changing text
    function updateText(selection, text) {
        if (text == null) {
            return null;
        }

        var temp = d3.select("body")
            .append("span")
            .classed("blocktip", true)
            .attr("visibility", "hidden")
            .html(text)
            .style("display", "inline-block")
            .style("max-width", "fit-content");

        var width = temp.node().getBoundingClientRect().width;
        temp.remove();

        selection.html(text);

        return (width > maxWidth) ? maxWidth : width;
    }

    // Updates position of selected tooltip (not used for sticky drag).
    function updatePosition(selection, text) {
        var width = updateText(selection, text);
        var bbox  = selection.node().getBoundingClientRect();
        var x = 0;
        var y = 0;

        var coord = d3.mouse(d3.select("body").node());

        // position below right of cursor
        x = coord[0] + 20;
        y = coord[1] + 5;

        // test if should position left of cursor
        if (x - window.pageXOffset + bbox.width > window.innerWidth) {
            x = coord[0] - bbox.width + 5;
        }

        // test if should position above cursor
        if (y - window.pageYOffset + bbox.height > window.innerHeight) {
            y = coord[1] - bbox.height + 10;
        }

        selection.style("left", "" + x + "px");
        selection.style("top",  "" + y + "px");
        selection.style("width", "" + width + "px");
    };

    // Updates position of sticky tooltip (being dragged).
    function dragPosition(selection) {
        var bbox  = selection.node().getBoundingClientRect();
        var x = 0;
        var y = 0;

        x = bbox.left + d3.event.dx; // bbox.left is relative to window
        y = bbox.top  + d3.event.dy; // bbox.top  is relative to window

        // page offset only greater than 0 if scrolled
        // otherwise, no offset is needed
        x += (window.pageXOffset > 0) ? window.pageXOffset : 0;
        y += (window.pageYOffset > 0) ? window.pageYOffset : 0;

        selection.style("left", "" + x + "px");
        selection.style("top",  "" + y + "px");
    };

    return main;
}

Clone this wiki locally