将带有 svg 的 html 表格添加到 d3 树形图的工具提示中

问题描述 投票:0回答:1

我正在做一个可视化 Web 应用程序,它使用 d3 树形图呈现数据。

当用户将鼠标悬停在树形图上时,会出现一个工具提示,将数据显示为具有许多行和列的表格,例如 5x4。

我可以正确显示每一行中的数据,但我很难将折线图显示到每行的一列中。

下面是我使用的代码:

// 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 = '\
    <h3 class="tooltip-header"> ' + name2 + ' / ' + name3 + '</h3> \
    <div class="tooltip-body"> \
    <table> \
  ';

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

    // 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 += ' \
        </tbody> \
      </table> \
    </div> \
    '
    
  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")
});

请帮忙!

javascript d3.js tooltip linegraph
1个回答
2
投票

潜在的核心问题是,您在

<svg>
中创建了
tooltip_template
,但从未为其分配任何维度(例如
height
width
)。

您可以进行一些额外的调整:

  • <div>
    中创建工具提示并在树形图的每个节点上重复使用它
  • 为要在悬停时显示的表格的每个
    <template>
    创建一个 <tr>(请参阅
    此处
  • 每个模板
    <tr>
    都有一个嵌入的
    <svg>
    ,它采用折线图并为每个
    class
  • 设置了尺寸
  • <table>
    的动态渲染展示在
    mouseenter
    ;更新每个鼠标在
    mousemove
    上的位置;并将桌子隐藏在
    mouseout

通过这些调整,您可以依赖 HTML 而不是字符串,并将表的计算移至调用一次而不是多次的事件处理程序。

下面的工作示例包含随机内容:

const leaf = d3.selectAll(".leaf");

leaf.on("mouseenter", function(d) {

  const table = d3.select("#custom-tooltip table")

  // remove any rows 
  table.selectAll("*").remove();

  // add random 2-5 data rows with content
  const rowCount = Math.floor(Math.random() * 4) + 2;
  
  for (let r=0; r<rowCount; ++r) {
    // add row
    const templateRow = document.querySelector("#tooltip-datarow").content.cloneNode(true);
    table.node().appendChild(templateRow);
    
    // add line chart
    const d3row = table.select("tr#datarow_x")
    d3row.attr("id", `datarow_${r}`);
    const linechart = d3row.select("svg.chartarea");

    linechart
      .append("path")
      .datum(linedata()) // randomised - replace with your data
      .attr("class", "line") 
      .attr("d", line);

    // add other details
    d3row.select(".label1").html(`Rand: ${Math.random().toFixed(2)}`);
    d3row.select(".label2").html(`Rand: ${Math.random().toFixed(2)}`);
    d3row.select(".label3").html(`Rand: ${Math.random().toFixed(2)}`);

    // show tooltip
    d3.select("#custom-tooltip")
      .style("visibility", "visible");

  }

    
});

leaf.on("mousemove", function(d) {

  // update tooltip with mouse 
  d3.select("#custom-tooltip")
    .style("top", (d3.event.pageY + 5) + "px")
    .style("left", (d3.event.pageX + 5) + "px")

});

leaf.on("mouseout", function(d) {

  // hide tooltip
  d3.selectAll("#custom-tooltip")
    .style("visibility", "hidden")
  
});
.leaf { width: 80px; height: 80px; fill: blue; }
.chartarea { width: 100px; height: 20px; }
.line { stroke: red; stroke-width: 1; fill: none; }
#custom-tooltip { position: absolute; z-index: 10; visibility: hidden; }
#custom-tooltip tr { background-color: #d6eeee; }
#tooltip-header { background-color: #eeD600; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<svg class="container">
  <rect class="leaf"></rect>
</svg>

<div id="custom-tooltip">
  <h3 id="tooltip-header">Header</h3>
  <div>
    <table>
    </table>  
  </div>
</div>

<template id="tooltip-datarow">
  <tr id="datarow_x">
    <td class="label1">Label</td>
    <td>
      <svg class="chartarea"></svg>
    </td>
    <td class="txtright label2">123</td>
    <td class="txtright label3">456</td>
  </tr>
</template>

<script>
// random data for line
function linedata() {
  return d3.range(10).map((_, i) => ({x: i, y: Math.floor(Math.random() * 10) + 1}));
}

// scales
const x = d3.scaleLinear().range([0, 100]).domain([0, 9]);
const y = d3.scaleLinear().range([0, 15]).domain([0, 9]);

// line generator
const line = d3
  .line()
  .x((d) => x(d.x))
  .y((d) => y(d.y));
     
</script>

© www.soinside.com 2019 - 2024. All rights reserved.