Demo目的:演示同一页面多个图表之间如何通过DOM事件与Highcharts API实现联动,悬浮提示框、十字准星同步;X轴缩放范围同步;重置缩放联动
功能概述
本示例实现页面多张图表联动效果:
- 鼠标悬浮任意一张图表,全部图表同步显示提示框(tooltip)与十字准星(crosshair)
- X轴缩放联动:对其中一个图表做X轴框选缩放,其余图表同步缩放X轴范围
- 重置缩放:一张图表重置缩放,其余图表同步复原
- 数据远程拉取
activity.json,动态循环生成多张独立图表,布局纵向排列。
核心原理:重写 Highcharts 内部指针、点位高亮原型方法,监听 DOM 鼠标事件、坐标轴
setExtremes事件完成多实例通信。
完整可运行HTML代码:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>多图表联动同步示例</title>
<script src="https://code.highcharts.com/highcharts.js"></script>
<style>
#container {
width: 95%;
margin: 0 auto;
}
.chart {
height: 220px;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
/*
Demo目的:演示同一页面多个图表之间如何通过DOM事件与Highcharts API实现联动
悬浮提示框、十字准星同步;X轴缩放范围同步;重置缩放联动
*/
/**
* 监听容器鼠标/触摸事件,实现全部图表tooltip、十字光标同步
*/
['mousemove', 'touchmove', 'touchstart'].forEach(function (eventType) {
document.getElementById('container').addEventListener(
eventType,
function (e) {
let chart, point, i, event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
// 将原生事件转换为图表内部坐标
event = chart.pointer.normalize(e);
// 根据坐标查找鼠标悬浮的数据点
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
}
);
});
/**
* 覆盖原生reset方法,禁止鼠标移出后自动隐藏tooltip与十字准星
*/
Highcharts.Pointer.prototype.reset = function () {
return undefined;
};
/**
* 扩展Point原型:高亮点位,显示悬浮标记、提示框、十字准星
*/
Highcharts.Point.prototype.highlight = function (event) {
event = this.series.chart.pointer.normalize(event);
this.onMouseOver(); // 激活点悬浮样式
this.series.chart.tooltip.refresh(this); // 刷新提示框
this.series.chart.xAxis[0].drawCrosshair(event, this); // 绘制十字准星
};
/**
* X轴范围同步回调:缩放其中一张图表,同步其余图表X轴区间
* 增加trigger标记避免循环调用
*/
function syncExtremes(e) {
const thisChart = this.chart;
if (e.trigger !== 'syncExtremes') {
Highcharts.charts.forEach(chart => {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) {
chart.xAxis[0].setExtremes(
e.min,
e.max,
undefined,
false,
{ trigger: 'syncExtremes' }
);
}
}
});
}
}
/**
* 图表重置缩放联动:一张图表复原,所有图表同步复原
*/
function resetZoom(e) {
if (e.resetSelection) {
return;
}
Highcharts.charts.forEach(chart => {
if (chart !== e.target) {
chart.zoomOut();
}
});
}
(async () => {
// 请求远程活动数据
const activity = await fetch(
'https://www.highcharts.com/samples/data/activity.json'
).then(res => res.json());
// 循环数据集,动态创建DOM容器并生成每一张图表
activity.datasets.forEach(function (dataset, i) {
// 将x轴里程与y值组合成 [[x,y]] 格式
dataset.data = dataset.data.map((val, j) => [activity.xData[j], val]);
const chartDiv = document.createElement('div');
chartDiv.className = 'chart';
document.getElementById('container').appendChild(chartDiv);
Highcharts.chart(chartDiv, {
chart: {
marginLeft: 40, // 统一左侧边距,图表左对齐
spacingTop: 20,
spacingBottom: 20,
zooming: {
type: 'x' // 仅支持X轴横向框选缩放
},
events: {
selection: resetZoom
}
},
title: {
text: dataset.name,
align: 'left',
margin: 0,
x: 30
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
crosshair: true,
events: {
setExtremes: syncExtremes
},
labels: {
format: '{value} 公里'
},
accessibility: {
description: '里程',
rangeDescription: '0公里 至 6.5公里'
}
},
yAxis: {
title: {
text: null
}
},
tooltip: {
fixed: true,
position: {
align: 'right',
relativeTo: 'spacingBox',
y: -2
},
padding: 0,
pointFormat: '{point.y}',
backgroundColor: 'none',
headerFormat: '',
shadow: false,
style: {
fontSize: '18px'
},
valueDecimals: dataset.valueDecimals
},
series: [{
data: dataset.data,
name: dataset.name,
type: dataset.type,
color: Highcharts.getOptions().colors[i],
fillOpacity: 0.3,
tooltip: {
valueSuffix: ' ' + dataset.unit
}
}]
});
});
})();
</script>
</body>
</html>
核心模块逐段解析
1、全局鼠标事件监听
监听外层容器 mousemove、触摸事件,遍历全局 Highcharts.charts 实例数组,对每一张图表查找鼠标位置对应的数据点,调用自定义 highlight()。
关键点:事件绑定在父容器,而不是单个图表,实现跨图表联动。
2、原型方法重写
Highcharts.Pointer.prototype.reset:屏蔽原生重置逻辑。原生鼠标移出图表会立刻隐藏tooltip和十字线;重写之后悬浮信息不会消失,达到多图同步显示效果。Highcharts.Point.prototype.highlight:自定义点位高亮方法,封装三件事:激活点悬浮样式、刷新tooltip、绘制十字准星。
⚠️注意:修改内置原型属于侵入式写法,如果页面其他图表不需要联动,会受到该全局修改影响。
3、坐标轴缩放同步 syncExtremes
setExtremes:X轴范围发生变化时触发事件。trigger: 'syncExtremes'标记,防止循环回调死循环:A修改B坐标轴,B触发事件又回改A。
4、resetZoom 缩放重置联动
当执行图表还原缩放,遍历全部图表调用 zoomOut(),实现全部图表一起复原。
5、动态批量创建图表
fetch获取远程JSON,包含多组指标数据集与X轴里程数组;- 循环数据集,JS动态创建div容器插入页面;
- 每个div实例化一张独立Highcharts图表;
- x轴单位改为中文"公里"。
业务场景与坑点总结
适用场景
- 监控大屏多指标时序面板(流量、性能多维度纵向排布)
- 运动轨迹多生理指标对比(本示例原始场景:距离‑心率‑海拔‑功率)
- IoT设备多传感器同步时序对比。
开发注意事项
- 原型修改是全局生效:页面如果同时存在不需要联动的普通图表,该demo会干扰它们tooltip行为;解决方案:隔离页面,或者封装子类不要直接修改原型。
- 依赖
Highcharts.charts全局实例数组;图表销毁时数组不会自动清理,长时间SPA页面要手动清理。 - 跨域问题:本地直接打开HTML访问
highcharts.com的json会跨域;本地调试建议把activity.json下载为本地模拟数据。 - 只支持X轴同步,如果需要Y轴同步,需要扩展
syncExtremes同时同步yAxis。
可扩展方向
- 增加开关控制是否开启联动;
- Y轴范围同步;
- 替换成本地静态模拟数据消除fetch跨域;
- 增加图表销毁清理逻辑适配SPA(Vue/React)。