Chart.js自定义分类X轴过滤:点击图例如何动态调整X轴范围?
问题解答
核心结论
isDatasetVisible和setDatasetVisibility仅负责控制数据集的显示/隐藏状态,不会自动对X轴标签进行子集化。要实现隐藏数据集后X轴自动收缩并仅保留可见数据对应的标签,必须手动处理数据子集并触发图表重绘,以下是具体实现方案:
实现步骤与代码修改
- 保存原始完整数据:避免多次修改后无法恢复初始状态
- 点击图例时动态过滤数据与X轴标签:根据当前可见的数据集,收集所有需要保留的X值,再过滤每个数据集的数据
- 更新图表并重新渲染
修改后的关键代码
首先在全局保存原始数据副本:
// 深拷贝原始数据,用于后续恢复与过滤 const originalData = JSON.parse(JSON.stringify(data));
然后修改图例点击事件的处理逻辑:
li.onclick = (click) => { const isHidden = !output.isDatasetVisible(index); output.setDatasetVisibility(index, isHidden); // 收集当前可见数据集的所有X轴标签 const visibleXLabels = new Set(); output.data.datasets.forEach((ds, dsIndex) => { if (output.isDatasetVisible(dsIndex)) { originalData.datasets[dsIndex].data.forEach(item => visibleXLabels.add(item.x)); } }); // 按原始数据中的顺序对X标签排序,保证轴顺序一致 const originalXOrder = originalData.datasets.flatMap(ds => ds.data.map(item => item.x)); const sortedLabels = Array.from(visibleXLabels).sort((a, b) => originalXOrder.indexOf(a) - originalXOrder.indexOf(b)); // 过滤每个数据集的数据,仅保留可见标签对应的项 output.data.datasets.forEach((ds, dsIndex) => { ds.data = originalData.datasets[dsIndex].data.filter(item => visibleXLabels.has(item.x)); }); updateLegend(click, output); };
完整修改后的JavaScript代码
var data = { datasets: [{ label: "Dataset #1", backgroundColor: 'red', data: [ {x: "A", y: 65}, {x: "B", y: 59}, {x: "C", y: 20} ], }, { label: "Dataset #2", backgroundColor: 'blue', data: [ {x: "D", y: 12}, {x: "E", y: 11}, {x: "F", y: 10} ], }] }; // 保存原始完整数据副本 const originalData = JSON.parse(JSON.stringify(data)); var option = { plugins: { legend: { display: false, } } }; function updateLegend(click, output) { const element = click.target.parentNode; element.classList.toggle('fade'); output.update(); } function generateLegend(output, container) { if (document.querySelectorAll('.customLegend').length === 0) { const chartBox = document.querySelector(container); const div = document.createElement('DIV'); div.setAttribute('class', 'customLegend'); const ul = document.createElement('UL'); output.legend.legendItems.forEach((dataset, index) => { const text = dataset.text; const stroke = dataset.strokeStyle; const fill = dataset.fillStyle; const fontColor = '#666'; const li = document.createElement('LI'); const spanBox = document.createElement('SPAN'); spanBox.style.borderColor = stroke; if (fill == 'rgba(0,0,0,0.1)') { spanBox.setAttribute('class', 'legend-annotation'); } else { spanBox.setAttribute('class', 'legend-content'); spanBox.style.backgroundColor = fill; } const p = document.createElement('P'); const textNode = document.createTextNode(text); li.onclick = (click) => { const isHidden = !output.isDatasetVisible(index); output.setDatasetVisibility(index, isHidden); // 收集当前可见数据集的所有X轴标签 const visibleXLabels = new Set(); output.data.datasets.forEach((ds, dsIndex) => { if (output.isDatasetVisible(dsIndex)) { originalData.datasets[dsIndex].data.forEach(item => visibleXLabels.add(item.x)); } }); // 按原始顺序排序X标签 const originalXOrder = originalData.datasets.flatMap(ds => ds.data.map(item => item.x)); const sortedLabels = Array.from(visibleXLabels).sort((a, b) => originalXOrder.indexOf(a) - originalXOrder.indexOf(b)); // 过滤每个数据集的数据 output.data.datasets.forEach((ds, dsIndex) => { ds.data = originalData.datasets[dsIndex].data.filter(item => visibleXLabels.has(item.x)); }); updateLegend(click, output); }; ul.appendChild(li); li.appendChild(spanBox); li.appendChild(p); p.appendChild(textNode); }); chartBox.prepend(div); div.appendChild(ul); } } const customLegend = { id: 'customLegend', afterDraw(chart, args, options) { generateLegend(chart, '.chart-container'); }, }; new Chart('chart_0', { type: 'bar', data: data, options: option, plugins: [customLegend], });
关键说明
- 原始数据保存:使用深拷贝避免修改原始数据,确保每次过滤都基于初始状态,防止多次操作后数据丢失
- X标签排序:基于原始数据中X的出现顺序排序,保证轴标签顺序与初始展示一致
- 动态过滤逻辑:每次点击图例时重新计算可见X标签,过滤对应数据集的数据,确保图表仅显示当前可见数据对应的轴标签
内容的提问来源于stack exchange,提问作者MayaGans
相关产品推荐
相关产品推荐

