Line chart + crosshair
Line chart that draws in, with a snapping crosshair tooltip
Techniques: pathLength · pointer-events · getBoundingClientRect · svg coordinate mapping
<!--
============================================================
SVGarden — https://www.svgarden.dev
Author : Simon-Pierre Boucher
Contact: contact@spboucher.ai
File : snippets/charts/chart-line-draw-tooltip.html
Desc : Line chart that draws in, with a snapping crosshair tooltip
============================================================
-->
<div class="sg-chart-line-draw-tooltip" style="--sg-color:#7F77DD; --sg-size:280px;">
<svg viewBox="0 0 240 120" role="img" aria-label="Line chart of monthly values rising from 24 to 82">
<title>Monthly values line chart</title>
<g class="sg-chart-line-draw-tooltip-grid">
<line x1="20" y1="30" x2="218" y2="30" />
<line x1="20" y1="56" x2="218" y2="56" />
<line x1="20" y1="82" x2="218" y2="82" />
<line x1="20" y1="104" x2="218" y2="104" />
</g>
<polyline class="sg-chart-line-draw-tooltip-line" pathLength="100"
points="20,82 42,72 64,77 86,61 108,66 130,52 152,45 174,50 196,37 218,30" />
<g class="sg-chart-line-draw-tooltip-hud">
<line class="sg-chart-line-draw-tooltip-cross" x1="20" y1="14" x2="20" y2="104" />
<circle class="sg-chart-line-draw-tooltip-dot" cx="20" cy="82" r="3.5" />
<text class="sg-chart-line-draw-tooltip-lbl" x="20" y="73">24</text>
</g>
</svg>
</div>
<script>
document.querySelectorAll('.sg-chart-line-draw-tooltip:not([data-sg-init])').forEach(function (root) {
root.setAttribute('data-sg-init', '');
var svg = root.querySelector('svg');
var hud = root.querySelector('.sg-chart-line-draw-tooltip-hud');
var cross = root.querySelector('.sg-chart-line-draw-tooltip-cross');
var dot = root.querySelector('.sg-chart-line-draw-tooltip-dot');
var lbl = root.querySelector('.sg-chart-line-draw-tooltip-lbl');
var pts = [[20,82,24],[42,72,36],[64,77,30],[86,61,48],[108,66,42],[130,52,58],[152,45,66],[174,50,60],[196,37,74],[218,30,82]];
svg.addEventListener('pointermove', function (e) {
var r = svg.getBoundingClientRect();
var x = (e.clientX - r.left) * 240 / r.width;
var best = pts[0];
for (var i = 1; i < pts.length; i++) if (Math.abs(pts[i][0] - x) < Math.abs(best[0] - x)) best = pts[i];
cross.setAttribute('x1', best[0]); cross.setAttribute('x2', best[0]);
dot.setAttribute('cx', best[0]); dot.setAttribute('cy', best[1]);
lbl.setAttribute('x', Math.max(28, Math.min(210, best[0])));
lbl.setAttribute('y', best[1] - 9);
lbl.textContent = best[2];
hud.style.opacity = 1;
});
svg.addEventListener('pointerleave', function () { hud.style.opacity = 0; });
});
</script>
<style>
.sg-chart-line-draw-tooltip {
width: var(--sg-size);
}
.sg-chart-line-draw-tooltip svg {
display: block;
width: 100%;
font-family: inherit;
touch-action: none;
}
.sg-chart-line-draw-tooltip-grid line {
stroke: rgba(128, 128, 128, 0.3);
stroke-width: 1;
stroke-dasharray: 2 4;
}
.sg-chart-line-draw-tooltip-line {
fill: none;
stroke: var(--sg-color);
stroke-width: 2.5;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 100;
stroke-dashoffset: 100;
animation: sg-chart-line-draw-tooltip-in 1.2s ease-out forwards;
}
@keyframes sg-chart-line-draw-tooltip-in {
to { stroke-dashoffset: 0; }
}
.sg-chart-line-draw-tooltip-hud {
opacity: 0;
transition: opacity 0.15s ease;
pointer-events: none;
}
.sg-chart-line-draw-tooltip-cross {
stroke: rgba(128, 128, 128, 0.5);
stroke-width: 1;
}
.sg-chart-line-draw-tooltip-dot {
fill: var(--sg-color);
}
.sg-chart-line-draw-tooltip-lbl {
font-size: 10px;
font-weight: 600;
fill: currentColor;
text-anchor: middle;
}
</style>