Interaction, resize and data updates
Event handling and dispatches, keeping the chart correct when the container changes size, and the merge rules behind setOption.
Events and actions
// listen: params carries the data point and the series that produced it
chart.on('click', (params) => {
console.log(params.seriesName, params.name, params.value, params.dataIndex);
openDetail(params.data.id);
});
chart.on('legendselectchanged', (params) => {
saveVisibility(params.selected); // { Free: false, Paid: true }
});
// dispatch: drive the chart programmatically
chart.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: 3 });
chart.dispatchAction({ type: 'showTip', seriesIndex: 0, dataIndex: 3 });
chart.dispatchAction({ type: 'dataZoom', start: 0, end: 50 });
chart.dispatchAction({ type: 'legendToggleSelect', name: 'Paid' });| Event | Fires when |
|---|---|
click / dblclick | A mark or an empty area is clicked; params.componentType says which |
mouseover / mouseout | Hover on a series item or axis |
legendselectchanged | A legend entry is toggled |
datazoom | The user zooms or pans |
finished | The render loop settled — the moment to screenshot the chart |
- Always
chart.off('click')before re-binding inside a render loop, or handlers accumulate. params.valueis the raw value;params.datais the original object you passed in, which is where custom fields live.- Actions are the supported way to script the chart — mutating internal state directly is not part of the public API.
Responsive resize
// simplest correct approach: observe the container, not the window
const ro = new ResizeObserver(() => chart.resize());
ro.observe(document.getElementById('main'));
// stop observing when the chart is torn down
// ro.disconnect(); chart.dispose();
// option-level responsiveness: overrides keyed on container width
chart.setOption({
baseOption: {
legend: { bottom: 0 },
series: [{ type: 'line', symbol: 'circle' }]
},
media: [
{
query: { maxWidth: 480 },
option: {
legend: { show: false },
yAxis: { splitNumber: 3 },
series: [{ symbolSize: 4 }]
}
}
]
});| Situation | What to do |
|---|---|
| Container width changed | chart.resize() via a ResizeObserver |
| Container became visible | chart.resize() after the tab or accordion opens |
| Fewer labels fit | axisLabel.rotate, axisLabel.hideOverlap, or a smaller splitNumber |
| Data needs a different form | media rules that swap series options wholesale |
| Device pixel ratio changed | Re-init with devicePixelRatio, or resize — ECharts re-reads it on resize |
Listening to window.resize misses every layout change that is not a viewport change: sidebars, split panes and CSS grid reflows. Observing the container is both simpler and more accurate.
Updating data
// merge (default): matched series are updated in place
chart.setOption({ series: [{ data: nextValues }] });
// explicitly replace one component type instead of merging
chart.setOption({ series: [{ data: nextValues }] }, { replaceMerge: ['series'] });
// full rebuild: nothing from the previous option survives
chart.setOption(freshOption, { notMerge: true });
// defer the render to the next frame when several updates arrive together
chart.setOption(patch, { lazyUpdate: true });
// streaming: append points instead of resending the whole array
chart.appendData({ seriesIndex: 0, data: [[t, v]] });| Call | Behaviour | Cost |
|---|---|---|
setOption(patch) | Deep merge into the current option | Cheapest — reuse it for value changes |
setOption(o, { replaceMerge: ['series'] }) | Replaces the listed components, keeps the rest | Moderate — the right call when series count changes |
setOption(o, { notMerge: true }) | Discards everything and rebuilds | Most expensive; resets zoom and animation state |
appendData() | Appends to the end of a line, bar or scatter series | Best for live streams |
clear() | Empties the canvas, keeps the instance | Use before loading a different chart type |
dispose() | Destroys the instance and frees listeners | Mandatory in single-page apps |
⚠️
A chart that is never disposed keeps its DOM listeners, timers and canvas alive. In a single-page app that swaps views, call
chart.dispose() in the teardown path — otherwise memory grows with every visit to the dashboard.FAQ
Why do my custom series settings vanish on the next update?
The component was replaced rather than merged, usually because the new option changed the series count or you passed
notMerge. Keep the option shape stable and patch only the arrays that change.How do I export a chart as an image?
Wait for the
finished event, then call chart.getDataURL({ pixelRatio: 2, backgroundColor: '#fff' }). The toolbox saveAsImage feature does the same from the UI.Related
Option configuration Setup and configuration
Last refreshed 2026-09-18.