click/mouseover 事件
ECharts 交互事件完全指南:从基础点击到复杂交互
📖 概述
ECharts 提供丰富的交互事件系统,支持鼠标事件、图表事件、行为事件等,让用户能够响应用户操作,实现动态交互效果。
核心事件类型:
- 🖱️ 鼠标事件: click, dblclick, mouseover, mouseout
- 📊 图表事件: legendselectchanged, datazoom, brush
- 🎯 行为事件: highlight, downplay, takeGlobalCursor
🔍 核心概念
1. 事件绑定基础
javascript
// 绑定事件
chart.on(eventName, handler);
// 解绑事件
chart.off(eventName, handler);
// 一次性事件
chart.once(eventName, handler);1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2. 鼠标事件
javascript
// 点击事件
chart.on('click', function(params) {
console.log('点击了:', params.name, params.value);
});
// 双击事件
chart.on('dblclick', function(params) {
console.log('双击了:', params);
});
// 鼠标悬停
chart.on('mouseover', function(params) {
console.log('悬停在:', params.name);
});
// 鼠标移出
chart.on('mouseout', function(params) {
console.log('移出了:', params.name);
});
// 右键菜单
chart.on('contextmenu', function(params) {
console.log('右键点击:', params);
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
3. 事件参数结构
javascript
// 事件回调函数接收的参数
{
// 组件类型
componentType: string, // 'series' | 'grid' | 'xAxis' | 'yAxis'
// 系列信息
seriesType: string, // 'line' | 'bar' | 'pie'
seriesIndex: number, // 系列索引
seriesName: string, // 系列名称
// 数据信息
name: string, // 数据项名称
dataIndex: number, // 数据索引
data: Object, // 数据对象
value: number|Array, // 数据值
// 其他
color: string, // 颜色
marker: string // 标记HTML
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
💡 使用场景
场景 1: 点击钻取(Drill-down)
javascript
class DrillDownChart {
constructor(dom) {
this.chart = echarts.init(dom);
this.currentLevel = 'province';
this.data = this.getProvinceData();
this.init();
}
init() {
this.render();
// 监听点击事件
this.chart.on('click', (params) => {
if (params.componentType === 'series') {
this.drillDown(params.name);
}
});
}
render() {
this.chart.setOption({
title: {
text: this.currentLevel === 'province' ? '各省销售数据' : `${this.currentLevel}各市销售数据`,
subtext: '点击柱状图查看详情'
},
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: this.data.map(d => d.name)
},
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: this.data.map(d => d.value),
itemStyle: {
cursor: 'pointer' // 显示手型光标
}
}]
});
}
drillDown(name) {
if (this.currentLevel === 'province') {
// 下钻到市级
this.currentLevel = name;
this.data = this.getCityData(name);
this.render();
// 添加返回按钮
this.addBackButton();
}
}
addBackButton() {
this.chart.setOption({
graphic: {
elements: [{
type: 'text',
left: 20,
top: 20,
style: {
text: '← 返回省级',
fontSize: 14,
fill: '#5470c6',
cursor: 'pointer'
},
onclick: () => {
this.currentLevel = 'province';
this.data = this.getProvinceData();
this.render();
}
}]
}
});
}
getProvinceData() {
return [
{ name: '广东', value: 12000 },
{ name: '江苏', value: 11000 },
{ name: '浙江', value: 9500 }
];
}
getCityData(province) {
const cityData = {
'广东': [
{ name: '广州', value: 4000 },
{ name: '深圳', value: 5000 },
{ name: '东莞', value: 3000 }
],
'江苏': [
{ name: '南京', value: 3500 },
{ name: '苏州', value: 4500 },
{ name: '无锡', value: 3000 }
]
};
return cityData[province] || [];
}
}
// 使用
new DrillDownChart(document.getElementById('chart'));1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
场景 2: 联动高亮
javascript
// 多个图表联动高亮
const charts = [chart1, chart2, chart3];
charts.forEach(chart => {
chart.on('mouseover', (params) => {
// 高亮所有图表的对应数据
charts.forEach(c => {
c.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: params.dataIndex
});
});
});
chart.on('mouseout', () => {
// 取消所有图表的高亮
charts.forEach(c => {
c.dispatchAction({
type: 'downplay',
seriesIndex: 0
});
});
});
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
场景 3: 自定义右键菜单
javascript
chart.on('contextmenu', (params) => {
// 阻止默认右键菜单
event.preventDefault();
// 显示自定义菜单
showCustomMenu(params, event.clientX, event.clientY);
});
function showCustomMenu(params, x, y) {
const menu = document.createElement('div');
menu.className = 'custom-menu';
menu.style.left = x + 'px';
menu.style.top = y + 'px';
menu.innerHTML = `
<div class="menu-item" data-action="detail">查看详情</div>
<div class="menu-item" data-action="export">导出数据</div>
<div class="menu-item" data-action="delete">删除此项</div>
`;
document.body.appendChild(menu);
// 菜单项点击事件
menu.querySelectorAll('.menu-item').forEach(item => {
item.addEventListener('click', () => {
const action = item.dataset.action;
handleMenuAction(action, params);
document.body.removeChild(menu);
});
});
// 点击其他地方关闭菜单
setTimeout(() => {
document.addEventListener('click', () => {
if (document.body.contains(menu)) {
document.body.removeChild(menu);
}
}, { once: true });
}, 0);
}
function handleMenuAction(action, params) {
switch(action) {
case 'detail':
alert(`详情: ${params.name} - ${params.value}`);
break;
case 'export':
exportData(params);
break;
case 'delete':
deleteData(params.dataIndex);
break;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
🔧 配置详解
常用事件列表
javascript
// 鼠标事件
const mouseEvents = [
'click', // 单击
'dblclick', // 双击
'mousedown', // 鼠标按下
'mouseup', // 鼠标释放
'mouseover', // 鼠标悬停
'mouseout', // 鼠标移出
'mousemove', // 鼠标移动
'mousewheel', // 鼠标滚轮
'contextmenu' // 右键菜单
];
// 图表事件
const chartEvents = [
'legendselectchanged', // 图例选择改变
'legendselected', // 图例选中
'legendunselected', // 图例取消选中
'datazoom', // 数据区域缩放
'datarangeselected', // 视觉映射范围选择
'timelinechanged', // 时间轴改变
'timelineplaychanged', // 时间轴播放状态改变
'restore', // 重置
'dataviewchanged', // 数据视图改变
'magictypechanged', // 动态类型切换
'geoselectchanged', // 地理选择改变
'pieselectchanged', // 饼图选择改变
'mapselectchanged' // 地图选择改变
];
// 行为事件
const actionEvents = [
'highlight', // 高亮
'downplay', // 取消高亮
'takeglobalcursor' // 获取全局光标
];1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
事件过滤
javascript
// 只监听特定系列
chart.on('click', { seriesName: '销售额' }, function(params) {
console.log('点击了销售额系列:', params.value);
});
// 只监听特定系列索引
chart.on('click', { seriesIndex: 0 }, function(params) {
console.log('点击了第一个系列:', params.value);
});
// 组合条件
chart.on('mouseover', {
seriesType: 'bar',
dataIndex: 0
}, function(params) {
console.log('悬停在第一个柱子上');
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
📝 代码示例
示例 1: 完整的事件监控系统
javascript
class EventMonitor {
constructor(dom) {
this.chart = echarts.init(dom);
this.eventLog = [];
this.maxLogs = 100;
this.init();
}
init() {
this.render();
this.bindEvents();
}
render() {
this.chart.setOption({
title: { text: '事件监控演示' },
tooltip: { trigger: 'axis' },
legend: { data: ['系列A', '系列B'] },
xAxis: { type: 'category', data: ['A', 'B', 'C', 'D', 'E'] },
yAxis: { type: 'value' },
series: [
{
name: '系列A',
type: 'bar',
data: [120, 200, 150, 80, 70]
},
{
name: '系列B',
type: 'line',
data: [60, 100, 75, 90, 110]
}
]
});
}
bindEvents() {
// 绑定所有鼠标事件
const events = ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'];
events.forEach(eventName => {
this.chart.on(eventName, (params) => {
this.logEvent(eventName, params);
});
});
// 绑定图表事件
this.chart.on('legendselectchanged', (params) => {
this.logEvent('legendselectchanged', params);
});
this.chart.on('datazoom', (params) => {
this.logEvent('datazoom', params);
});
}
logEvent(eventName, params) {
const log = {
time: new Date().toLocaleTimeString(),
event: eventName,
seriesName: params.seriesName,
dataIndex: params.dataIndex,
value: params.value
};
this.eventLog.unshift(log);
// 限制日志数量
if (this.eventLog.length > this.maxLogs) {
this.eventLog.pop();
}
console.log(`[${log.time}] ${eventName}:`, params);
}
getEventLogs() {
return this.eventLog;
}
clearLogs() {
this.eventLog = [];
}
}
// 使用
const monitor = new EventMonitor(document.getElementById('chart'));1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
示例 2: 交互式数据编辑
javascript
class EditableChart {
constructor(dom, data) {
this.chart = echarts.init(dom);
this.data = data;
this.editingIndex = null;
this.init();
}
init() {
this.render();
// 双击进入编辑模式
this.chart.on('dblclick', (params) => {
if (params.componentType === 'series') {
this.startEdit(params.dataIndex);
}
});
}
render() {
this.chart.setOption({
title: {
text: '可编辑图表',
subtext: '双击柱子修改数值'
},
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: this.data.map(d => d.name)
},
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: this.data.map(d => ({
value: d.value,
itemStyle: {
color: d.index === this.editingIndex ? '#ee6666' : '#5470c6'
}
}))
}]
});
}
startEdit(index) {
this.editingIndex = index;
this.render();
// 弹出输入框
const newValue = prompt(
`修改 "${this.data[index].name}" 的值:`,
this.data[index].value
);
if (newValue !== null && !isNaN(newValue)) {
this.data[index].value = Number(newValue);
this.editingIndex = null;
this.render();
// 触发自定义事件
this.chart.dispatchAction({
type: 'datavaluechanged',
dataIndex: index,
newValue: Number(newValue)
});
} else {
this.editingIndex = null;
this.render();
}
}
}
// 使用
const editableChart = new EditableChart(
document.getElementById('chart'),
[
{ name: '产品A', value: 120 },
{ name: '产品B', value: 200 },
{ name: '产品C', value: 150 }
]
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
⚠️ 常见问题
Q1: 事件不触发?
javascript
// ✅ 确保在图表初始化后绑定事件
const chart = echarts.init(dom);
chart.setOption(option);
// 然后绑定事件
chart.on('click', handler);
// ❌ 不要在初始化前绑定
chart.on('click', handler); // 可能无效
chart.setOption(option);1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Q2: 如何获取点击位置的数据?
javascript
chart.on('click', (params) => {
// params 包含所有需要的信息
console.log('系列名:', params.seriesName);
console.log('数据名:', params.name);
console.log('数据值:', params.value);
console.log('数据索引:', params.dataIndex);
console.log('原始数据:', params.data);
});1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Q3: 如何阻止默认行为?
javascript
chart.on('contextmenu', (params) => {
event.preventDefault(); // 阻止默认右键菜单
// 自定义处理
});1
2
3
4
2
3
4
🎯 最佳实践
1. 事件清理
javascript
// 组件卸载时解绑事件
componentWillUnmount() {
chart.off('click');
chart.off('mouseover');
chart.dispose();
}1
2
3
4
5
6
2
3
4
5
6
2. 性能优化
javascript
// 高频事件使用节流
let lastTime = 0;
chart.on('mousemove', (params) => {
const now = Date.now();
if (now - lastTime < 100) return; // 100ms节流
lastTime = now;
handleMouseMove(params);
});1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
3. 错误处理
javascript
chart.on('click', (params) => {
try {
handleClick(params);
} catch (error) {
console.error('事件处理失败:', error);
}
});1
2
3
4
5
6
7
2
3
4
5
6
7
📊 性能指标
| 事件类型 | 触发频率 | 建议 |
|---|---|---|
| click | 低频 | 无限制 |
| mouseover | 高频 | 使用节流 |
| mousemove | 极高频 | 必须节流/防抖 |
| datazoom | 中频 | 正常 |
🔗 相关链接
最后更新: 2026-04-23
难度等级: ⭐⭐
预计阅读时间: 18 分钟
示例演示
{
"title": {
"text": "事件监控演示"
},
"tooltip": {
"trigger": "axis"
},
"legend": {
"data": [
"系列A",
"系列B"
]
},
"xAxis": {
"type": "category",
"data": [
"A",
"B",
"C",
"D",
"E"
]
},
"yAxis": {
"type": "value"
},
"series": [
{
"name": "系列A",
"type": "bar",
"data": [
120,
200,
150,
80,
70
]
},
{
"name": "系列B",
"type": "line",
"data": [
60,
100,
75,
90,
110
]
}
]
}