You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chart.js自定义分类X轴过滤:点击图例如何动态调整X轴范围?

问题解答

核心结论

isDatasetVisible和setDatasetVisibility仅负责控制数据集的显示/隐藏状态,不会自动对X轴标签进行子集化。要实现隐藏数据集后X轴自动收缩并仅保留可见数据对应的标签,必须手动处理数据子集并触发图表重绘,以下是具体实现方案:

实现步骤与代码修改

  1. 保存原始完整数据:避免多次修改后无法恢复初始状态
  2. 点击图例时动态过滤数据与X轴标签:根据当前可见的数据集,收集所有需要保留的X值,再过滤每个数据集的数据
  3. 更新图表并重新渲染

修改后的关键代码

首先在全局保存原始数据副本:

// 深拷贝原始数据,用于后续恢复与过滤
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 19:20:32