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

如何在Chart.js中将数据过多的Tooltip内容拆分为两列?

Chart.js实现Tooltip两列布局及加宽方案

这个需求完全可以实现,Chart.js支持通过自定义Tooltip的渲染逻辑来实现多列布局,核心是利用tooltip.custom回调函数自定义HTML结构,配合CSS控制样式,同时保留你原有配置中的样式细节。

修改后的配置代码

结合你现有的Tooltip配置,添加自定义渲染逻辑后的完整插件配置如下:

plugins: {
  tooltip: {
    caretSize: 10,
    cornerRadius: 30,
    bodySpacing: 10,
    mode: "index",
    usePointStyle: true,
    boxWidth: 10,
    boxHeight: 10,
    boxPadding: 10,
    padding: 20,
    intersect: false,
    titleFont: {
      size: CHART_STYLE_CONFIG.tooltipTitleFontSize,
    },
    titleColor: COLORS.TEXT_PRIMARY,
    bodyFont: {
      size: CHART_STYLE_CONFIG.tooltipBodyFontSize,
      weight: 400,
    },
    bodyColor: COLORS.TEXT_PRIMARY,
    backgroundColor: "#FFFFFF",
    // 启用自定义Tooltip渲染
    enabled: true,
    custom: function(context) {
      // 创建或获取自定义Tooltip容器
      let tooltipEl = document.getElementById('chart-custom-tooltip');
      if (!tooltipEl) {
        tooltipEl = document.createElement('div');
        tooltipEl.id = 'chart-custom-tooltip';
        tooltipEl.style.cssText = `
          position: absolute;
          background: ${this.options.backgroundColor};
          border-radius: ${this.options.cornerRadius}px;
          padding: ${this.options.padding}px;
          width: 400px; /* 自定义加宽宽度,可按需调整 */
          box-shadow: 0 2px 10px rgba(0,0,0,0.1);
          pointer-events: none;
          display: none;
          flex-direction: column;
        `;
        document.body.appendChild(tooltipEl);
      }

      // 无数据时隐藏Tooltip
      if (context.tooltip.opacity === 0) {
        tooltipEl.style.display = 'none';
        return;
      }

      // 设置Tooltip定位
      tooltipEl.style.left = context.tooltip.x + 'px';
      tooltipEl.style.top = context.tooltip.y + 'px';
      tooltipEl.style.display = 'block';

      // 获取Tooltip标题和数据项
      const title = context.tooltip.title[0];
      const items = context.tooltip.items;

      // 拆分数据为左右两列
      const midIndex = Math.ceil(items.length / 2);
      const leftItems = items.slice(0, midIndex);
      const rightItems = items.slice(midIndex);

      // 生成标题元素
      const titleEl = document.createElement('div');
      titleEl.style.cssText = `
        font-size: ${this.options.titleFont.size}px;
        color: ${this.options.titleColor};
        margin-bottom: 15px;
        font-weight: bold;
      `;
      titleEl.textContent = title;

      // 生成左列内容
      const leftColumn = document.createElement('div');
      leftColumn.style.cssText = 'flex: 1;';
      leftItems.forEach(item => {
        const row = document.createElement('div');
        row.style.cssText = `
          display: flex;
          align-items: center;
          gap: 10px;
          margin-bottom: ${this.options.bodySpacing}px;
          font-size: ${this.options.bodyFont.size}px;
          color: ${this.options.bodyColor};
        `;
        // 渲染点样式
        const point = document.createElement('span');
        point.style.cssText = `
          width: ${this.options.boxWidth}px;
          height: ${this.options.boxHeight}px;
          border-radius: 50%;
          background: ${item.backgroundColor};
          display: inline-block;
        `;
        // 渲染标签和数值
        const label = document.createElement('span');
        label.textContent = `${item.label}: ${item.formattedValue}`;
        row.appendChild(point);
        row.appendChild(label);
        leftColumn.appendChild(row);
      });

      // 生成右列内容
      const rightColumn = document.createElement('div');
      rightColumn.style.cssText = 'flex: 1;';
      rightItems.forEach(item => {
        const row = document.createElement('div');
        row.style.cssText = `
          display: flex;
          align-items: center;
          gap: 10px;
          margin-bottom: ${this.options.bodySpacing}px;
          font-size: ${this.options.bodyFont.size}px;
          color: ${this.options.bodyColor};
        `;
        const point = document.createElement('span');
        point.style.cssText = `
          width: ${this.options.boxWidth}px;
          height: ${this.options.boxHeight}px;
          border-radius: 50%;
          background: ${item.backgroundColor};
          display: inline-block;
        `;
        const label = document.createElement('span');
        label.textContent = `${item.label}: ${item.formattedValue}`;
        row.appendChild(point);
        row.appendChild(label);
        rightColumn.appendChild(row);
      });

      // 组装Tooltip内容
      tooltipEl.innerHTML = '';
      tooltipEl.appendChild(titleEl);
      const contentWrapper = document.createElement('div');
      contentWrapper.style.cssText = 'display: flex; gap: 20px;';
      contentWrapper.appendChild(leftColumn);
      contentWrapper.appendChild(rightColumn);
      tooltipEl.appendChild(contentWrapper);
    }
  },
}

关键实现说明

  • 加宽Tooltip:通过设置自定义容器的width: 400px实现,可根据实际需求调整数值
  • 两列布局:使用CSS Flex布局将拆分后的左右数据列并排显示,通过gap控制列间距
  • 保留原有样式:复用了你配置中的字体、颜色、间距、点样式等参数,保持风格一致
  • 动态定位:通过context.tooltip.x/y获取Tooltip的定位坐标,确保显示位置正确

注意事项

  • 如果页面存在多个图表,建议给每个Tooltip容器设置唯一ID(如chart1-custom-tooltip),避免DOM冲突
  • 可根据需求调整列宽、间距、阴影等样式细节
  • 若需要响应式适配,可在custom函数中根据窗口宽度动态调整Tooltip宽度

内容的提问来源于stack exchange,提问作者Talya Sterman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:10:36