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

在D3树形图Tooltip中添加含SVG折线图的HTML表格

问题:D3 Tooltip表格内嵌折线图无法显示

我正在开发一个基于D3 treemap的可视化Web应用,当用户悬停树形图节点时,Tooltip会以表格形式展示多行数据(比如5×4结构)。目前普通文本列能正常显示,但每行指定列内的折线图无法渲染出来,当前使用的代码如下:

// Tooltip
leaf.on("mousemove", function (d) {
  if (d.depth < 3) return;
  
  var name1 = data.name;
  var name2 = d.parent.data.name;
  var name3 = d.data.name;
  var category = d.data.category;
  var change = d.data.change;
  var filtered = items.filter(function (item) { return item.level2 == d.parent.data.name });

  var tooltip_template = '\
    &lt;h3 class="tooltip-header"&gt; ' + name2 + ' / ' + name3 + '&lt;/h3&gt; \
    &lt;div class="tooltip-body"&gt; \
    &lt;table&gt; \
  ';

  filtered.forEach(function (item) {
    tooltip_template += item.name == d.data.name
      ? '&lt;tr style="color: #fff; background: ' + getColor(d.data.change) + '"&gt; \
           &lt;td&gt; ' + item.name + '&lt;/td&gt; \
           &lt;td&gt; \
             &lt;svg&gt;&lt;/svg&gt; \
           &lt;/td&gt; \
           &lt;td class="txtright"&gt; ' + item.value + ' 건&lt;/td&gt; \
           &lt;td class="txtright"&gt; ' + (item.change > 0 ? '+' : '') + item.change + ' 건&lt;/td&gt; \
         &lt;/tr&gt;'
      : '&lt;tr&gt; \
           &lt;td&gt; ' + item.name + '&lt;/td&gt; \
           &lt;td&gt; \
             &lt;svg&gt;&lt;/svg&gt; \
           &lt;/td&gt; \
           &lt;td class="txtright"&gt; ' + item.value + ' 건&lt;/td&gt; \
           &lt;td class="txtright"&gt; ' + (item.change > 0 ? '+' : '') + item.change + ' 건&lt;/td&gt; \
         &lt;/tr&gt;';

    // set the dimensions and margins of the line graph
    var linemargin = {
      top: d3.event.pageY + 15,
      right: d3.event.pageX + 5,
      bottom: d3.event.pageY + 15,
      left: d3.event.pageX + 5
    }, linewidth = 100 - linemargin.left - linemargin.right, lineheight = 20 - linemargin.top - linemargin.bottom;

    // Set the data for the chart
    const linedata = [
      { x: 0, y: 5 },
      { x: 1, y: 9 },
      { x: 2, y: 7 },
      { x: 3, y: 5 },
      { x: 4, y: 3 },
      { x: 5, y: 4 },
      { x: 6, y: 8 },
      { x: 7, y: 6 },
      { x: 8, y: 3 },
      { x: 9, y: 2 },
    ];

    const linechart = tooltip.selectAll("svg").append("g").enter().attr("class", "linechart").attr("transform", `translate(${linemargin.left}, ${linemargin.top})`);

    const x = d3.scaleLinear().domain([0, 9]).range([0, linewidth]);
    const y = d3.scaleLinear().domain([0, 10]).range([lineheight, 0]);

    // Create the line generator
    const line = d3
      .line()
      .x((d) => x(d.x))
      .y((d) => y(d.y));

    // Add the line to the chart
    linechart.append("path").datum(linedata).attr("class", "line").attr("d", line);
  })

  tooltip_template += ' \
        &lt;/tbody&gt; \
      &lt;/table&gt; \
    &lt;/div&gt; \
    '
    
  tooltip
    .attr('id', 'tooltip')
    .attr('data-change', change)
    .style("", "#")

  tooltip.transition()      
    .style("opacity", 1);

  tooltip.html(tooltip_template) 
    .style("left", (d3.event.pageX + 5) + "px")
    .style("top", (d3.event.pageY + 15) + "px")
});

问题分析与修复方案

核心问题

  1. DOM操作顺序错误:先拼接HTML字符串再尝试绘制折线图,但此时SVG元素尚未插入页面,D3无法选中目标元素。
  2. 尺寸计算逻辑错误:将鼠标坐标作为SVG内部边距,导致计算出的宽高为负数,折线图无渲染空间。
  3. 选择器与数据绑定错误:全局选择所有SVG会导致折线图绘制混乱,且append("g").enter()不符合D3数据绑定逻辑。

修复后的完整代码

// Tooltip
leaf.on("mousemove", function (d) {
  if (d.depth < 3) return;
  
  const name2 = d.parent.data.name;
  const name3 = d.data.name;
  const change = d.data.change;
  const filtered = items.filter(item => item.level2 === d.parent.data.name);

  // 构建HTML模板框架(仅包含空SVG标签)
  let tooltip_template = `
    <h3 class="tooltip-header">${name2} / ${name3}</h3>
    <div class="tooltip-body">
      <table>
        <tbody>
  `;

  // 添加表格行
  filtered.forEach(item => {
    const isActive = item.name === d.data.name;
    const rowStyle = isActive ? `style="color: #fff; background: ${getColor(d.data.change)}"` : '';
    tooltip_template += `
      <tr ${rowStyle}>
        <td>${item.name}</td>
        <td><svg class="line-svg"></svg></td>
        <td class="txtright">${item.value} 건</td>
        <td class="txtright">${item.change > 0 ? '+' : ''}${item.change} 건</td>
      </tr>
    `;
  });

  tooltip_template += `
        </tbody>
      </table>
    </div>
  `;

  // 先插入HTML到Tooltip,确保DOM元素存在后再绘制折线图
  tooltip
    .attr('id', 'tooltip')
    .attr('data-change', change)
    .style("opacity", 0)
    .html(tooltip_template)
    .style("left", `${d3.event.pageX + 5}px`)
    .style("top", `${d3.event.pageY + 15}px`)
    .transition()
    .style("opacity", 1);

  // 遍历每个SVG元素,绘制对应折线图
  filtered.forEach((item, index) => {
    // 设置SVG内部边距与尺寸
    const linemargin = { top: 2, right: 2, bottom: 2, left: 2 };
    const linewidth = 100 - linemargin.left - linemargin.right;
    const lineheight = 20 - linemargin.top - linemargin.bottom;

    // 获取当前行的SVG元素并设置基础尺寸
    const svg = d3.select(tooltip.selectAll('.line-svg').nodes()[index])
      .attr('width', 100)
      .attr('height', 20);

    // 创建绘图容器
    const linechart = svg.append('g')
      .attr('transform', `translate(${linemargin.left}, ${linemargin.top})`);

    // 模拟折线图数据(可替换为item对应的真实数据)
    const linedata = [
      { x: 0, y: 5 }, { x: 1, y: 9 }, { x: 2, y: 7 }, { x: 3, y: 5 },
      { x: 4, y: 3 }, { x: 5, y: 4 }, { x: 6, y: 8 }, { x: 7, y: 6 },
      { x: 8, y: 3 }, { x: 9, y: 2 }
    ];

    // 定义比例尺
    const x = d3.scaleLinear().domain([0, 9]).range([0, linewidth]);
    const y = d3.scaleLinear().domain([0, 10]).range([lineheight, 0]);

    // 创建折线生成器
    const line = d3.line()
      .x(d => x(d.x))
      .y(d => y(d.y));

    // 添加折线并设置可见样式
    linechart.append('path')
      .datum(linedata)
      .attr('class', 'line')
      .attr('fill', 'none')
      .attr('stroke', '#333')
      .attr('stroke-width', 1.5)
      .attr('d', line);
  });
});

关键修复点

  1. 调整DOM操作顺序:先插入完整HTML模板(含空SVG),确保元素存在后再进行折线图绘制。
  2. 修正尺寸计算:使用SVG内部小边距,保证宽高为正数,提供有效渲染空间。
  3. 精准匹配SVG元素:通过索引绑定每行对应的SVG,避免绘制混乱。
  4. 添加折线基础样式:设置fill: none、stroke等属性,确保折线可见。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 20:47:34