Skip to content

Instantly share code, notes, and snippets.

@robert-moore
Created January 27, 2018 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robert-moore/38ebc00b8bb2ca33e85b8c415cbe03b0 to your computer and use it in GitHub Desktop.
Save robert-moore/38ebc00b8bb2ca33e85b8c415cbe03b0 to your computer and use it in GitHub Desktop.
D3 Tooltips
// generates innerHTML for tooltip based on title and a map of key:value pairs
export function keyValueTitleTooltip(tooltip, title, map) {
tooltip.classed("key-value-tooltip", true).html("")
if (title)
tooltip
.append("p")
.attr("class", "title")
.text(title)
tooltip
.selectAll("p.value")
.data(Object.keys(map))
.enter()
.append("p")
.attr("class", "value")
.html(d => `<b>${d}:</b> ${map[d]}`)
}
// generates innerHTML for tooltip bar chart comparing values
// [ { name: focalName, value: focalValue formattedValue: focalDisplay }, { name: compareName ... }]
export function comparisonBarChartTooltip(tooltip, title, data) {
tooltip.classed("bar-chart-tooltip", true).html("")
tooltip.style("min-width", "200px")
let x = d3
.scaleLinear()
.range([0, 170])
.domain([0, d3.max(data, d => d.value)])
if (title) tooltip.append("p".attr("class", "title")).text(title)
tooltip
.append("p")
.attr("class", "name")
.text(data[0].name)
for (let i = 0; i < 2; i++) {
let valueContainer = tooltip
.append("div")
.attr("class", "value-container")
.classed("focal", i === 1)
valueContainer
.append("div")
.attr("class", "value-bar")
.style("width", x(data[i].value) + "px")
.style("height", "30px")
valueContainer
.append("p")
.attr("class", "value-label")
.text(data[i].formattedValue || data[i].value)
}
tooltip
.append("p")
.attr("class", "name")
.text(data[1].name)
}
// places tooltip within container with config options
export function placeTooltip(
tooltip,
container,
{
verticalAnchor = "bottom",
horizontalAnchor = "center",
x = 0,
y = 0,
position = "relative",
} = {},
) {
// svg is the container used for absolute positioning and for setting a bounding box
const contentRect = tooltip.node().getBoundingClientRect()
const containerRect = container.node().getBoundingClientRect()
const contentWidth = contentRect.width
let tooltipLeftEdge = x
if (position === "relative") tooltipLeftEdge += containerRect.left
if (horizontalAnchor === "center") tooltipLeftEdge -= contentWidth / 2
if (horizontalAnchor === "right") tooltipLeftEdge -= contentWidth
const containerRightEdge = containerRect.left + containerRect.width - 5 // padding
// If the right edge of tooltip is beyond the right edge of the container,
// fix the tooltip to the edge of the container:
if (tooltipLeftEdge > containerRightEdge - contentWidth) {
tooltipLeftEdge = containerRightEdge - contentWidth
} else if (tooltipLeftEdge < containerRect.left) {
tooltipLeftEdge = containerRect.left
}
let tooltipTopEdge = y + window.scrollY
if (position === "relative") tooltipTopEdge += containerRect.top
if (verticalAnchor === "bottom") tooltipTopEdge -= contentRect.height
if (tooltipTopEdge < containerRect.top) {
tooltipTopEdge = containerRect.top
}
tooltip
.style("top", `${tooltipTopEdge}px`)
.style("left", `${tooltipLeftEdge}px`)
.style("visibility", "visible")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment