Механизм всплывающих подсказок в экосистеме Vega и Vega-Lite реализуется через отдельный слой взаимодействия, построенный вокруг библиотеки vega-tooltip. Эта библиотека отвечает за отображение контекстной информации при наведении курсора на визуальные элементы графика и тесно интегрируется с системой событий Vega Runtime.
В Vega/Vega-Lite тултипы не являются частью базового рендеринга. Они формируются на уровне взаимодействий:
mouseover,
mouseout, mousemove)vega-tooltip форматирует содержимоеКлючевая особенность: тултип отделён от графической сцены и живёт в DOM независимо от визуализации.
Подключение в классическом сценарии через
vega-embed:
import embed from 'vega-embed';
import { handler } from 'vega-tooltip';
const spec = {
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
data: { values: [
{category: 'A', value: 28},
{category: 'B', value: 55}
]},
mark: 'bar',
encoding: {
x: {field: 'category', type: 'nominal'},
y: {field: 'value', type: 'quantitative'}
}
};
embed('#vis', spec, {
tooltip: new handler().call
});
Здесь handler() создаёт обработчик, который связывает
события Vega с DOM-слоем тултипов.
vega-tooltip автоматически извлекает значения из
datum. По умолчанию отображается сериализованный объект, но
форматирование контролируется через конфигурацию.
{
"category": "A",
"value": 28
}
Отображается как:
category: A
value: 28
vega-tooltip поддерживает кастомизацию через параметры
обработчика:
import { handler } from 'vega-tooltip';
const tooltipHandler = new handler({
formatTooltip: (value, sanitize) => {
if (typeof value === 'object') {
return Object.entries(value)
.map(([k, v]) => `<b>${k}</b>: ${sanitize(v)}`)
.join('<br/>');
}
return sanitize(value);
}
});
Функция sanitize предотвращает XSS при использовании
HTML в тултипах. Любая строка проходит через фильтр:
Vega позволяет включать HTML-разметку в tooltip при явном разрешении.
const tooltipHandler = new handler({
formatTooltip: (value, sanitize) => {
return `
<div class="tooltip">
<div><b>Значение:</b> ${sanitize(value.value)}</div>
<div><i>Категория:</i> ${sanitize(value.category)}</div>
</div>
`;
}
});
HTML не включается автоматически. Только через
formatTooltip, где разработчик полностью контролирует
структуру.
Vega-Lite поддерживает упрощённое объявление тултипов без прямого
обращения к vega-tooltip.
{
"mark": "point",
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "y", "type": "quantitative"},
"tooltip": [
{"field": "x", "type": "quantitative"},
{"field": "y", "type": "quantitative"}
]
}
}
В этом случае Vega-Lite:
vega-tooltipencoding.tooltipvega-embedВ Vega можно полностью заменить стандартную механику:
{
"signals": [
{
"name": "tooltip",
"value": {},
"on": [
{"events": "symbol:mouseover", "update": "datum"},
{"events": "symbol:mouseout", "update": "{}"}
]
}
]
}
Далее внешний обработчик:
view.addSignalListener('tooltip', (name, value) => {
tooltipHandler.call(null, value, {});
});
vega-tooltip рендерит HTML в контейнер с фиксированным
классом.
Основной контейнер:
.vega-tooltip {
position: absolute;
background: #1e1e1e;
color: #fff;
padding: 8px 10px;
border-radius: 4px;
font-family: sans-serif;
font-size: 12px;
pointer-events: none;
}
Дополнительные состояния:
.vega-tooltip.active {
opacity: 1;
transform: translate(0, 0);
}
Позиционирование рассчитывается автоматически на основе координат события мыши:
pageX, pageY события DOMАлгоритм включает:
При работе с тысячами точек:
Оптимизации:
canvas вместо svgЧастая задача — контроль отображения чисел:
const tooltipHandler = new handler({
formatTooltip: (value, sanitize) => {
if (typeof value === 'number') {
return value.toFixed(2);
}
return sanitize(value);
}
});
Распространённый подход — шаблонизация:
const template = (d) => `
<div>
<div>Категория: ${d.category}</div>
<div>Значение: ${d.value}</div>
</div>
`;
Использование:
formatTooltip: (value, sanitize) => template(value)
При работе с facet, repeat,
concat:
row,
column, datum)Пример структуры:
{
"row": "North",
"column": "2024",
"datum": {
"value": 42
}
}
При вложенных объектах стандартный tooltip может становиться перегруженным. Используется выборочное отображение:
formatTooltip: (value, sanitize) => {
return `
Region: ${value.row}<br/>
Year: ${value.column}<br/>
Value: ${value.datum.value}
`;
}
vega-tooltip может быть заменён или расширен:
Пример интеграции с внешним рендерингом:
const handler = new Handler({
formatTooltip: (value) => {
externalTooltip.render(value);
return '';
}
});
Если datum отсутствует или пуст:
mouseout принудительно очищает состояниеВ Vega-Lite агрегаты часто отображаются в tooltip автоматически:
{
"aggregate": "sum",
"field": "value"
}
В тултипе:
sum(value): 128
Формат можно переопределить через formatTooltip.
При обновлении данных через view.change():