D3js:如何让我的工具提示出现在选择的旁边?

D3js: How do I make my tooltip appear next to the selection?

我正在尝试为我的热图可视化上的每个方块提供工具提示。但是,我无法将它正确定位在光标旁边。这是发生了什么:

我的代码是这样的:

const margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 150,
};
const width = 960 - margin.left - margin.right;
const height = 2500 - margin.top - margin.bottom;

let chart = d3
  .select("#chart2")
  .append("div")
  // Set id to chartArea
  .attr("id", "chartArea")
  .classed("chart", true)
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Make sure to create a separate SVG for the XAxis
let axis = d3
  .select("#chart2")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", 40)
  .append("g")
  .attr("transform", "translate(" + margin.left + ", 0)");

// Load the data
d3.csv("https://raw.githubusercontent.com/thedivtagguy/files/main/land_use.csv").then(function(data) {
  // console.log(data);
  const years = Array.from(new Set(data.map((d) => d.year)));
  const countries = Array.from(new Set(data.map((d) => d.entity)));
  countries.reverse();
  const x = d3.scaleBand().range([0, width]).domain(years).padding(0.01);

  // create a tooltip
  var tooltip = d3
    // Select all boxes in the chart
    .select("#chart2")
    .append("div")
    .classed("tooltip", true)
    .style("opacity", 0)
    .attr("class", "tooltip")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "2px")
    .style("border-radius", "5px")
    .style("padding", "5px");

  const mouseover = function(event, d) {
    tooltip.style("opacity", 1);
    d3.select(this).style("stroke", "black").style("opacity", 1);
  };

  const mousemove = function(event, d) {
    tooltip
      .html("The exact value of<br>this cell is: " + d.value)
      // Position the tooltip next to the cursor
      .style("left", event.x + "px")
      .style("top", event.y / 2 + "px");
  };

  const mouseleave = function(event, d) {
    tooltip.style("opacity", 0);
    d3.select(this).style("stroke", "none").style("opacity", 0.8);
  };

  const y = d3.scaleBand().range([height, 0]).domain(countries).padding(0.01);

  // Only 10 years
  axis
    .call(d3.axisBottom(x).tickValues(years.filter((d, i) => !(i % 10))))
    .selectAll("text")
    .style("color", "black")
    .style("position", "fixed")
    .attr("transform", "translate(-10,10)rotate(-45)")
    .style("text-anchor", "end");

  chart
    .append("g")
    .call(d3.axisLeft(y))
    .selectAll("text")
    .style("color", "black")
    .attr("transform", "translate(-10,0)")
    .style("text-anchor", "end");

  const colorScale = d3
    .scaleSequential()
    .domain([0, d3.max(data, (d) => d.change)])
    .interpolator(d3.interpolateInferno);

  // add the squares
  chart
    .selectAll()
    .data(data, function(d) {
      return d.year + ":" + d.entity;
    })
    .join("rect")
    // Add id-s
    .attr("id", function(d) {
      return d.year + ":" + d.entity;
    })
    .attr("x", function(d) {
      return x(d.year);
    })
    .attr("y", function(d) {
      return y(d.entity);
    })
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function(d) {
      return colorScale(d.change);
      console.log(d.change);
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8)
    .on("mouseover", mouseover)
    .on("mousemove", mousemove)
    .on("mouseleave", mouseleave);
});
#chart2 .chart {
  width: 960px;
  max-height: 900px;
  overflow-y: scroll;
  overflow-x: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<div id="chart2" class="mx-auto"></div>

我想我知道为什么会这样。因为我像这样将 tooltip iv 附加到 chart2 div,所以所有定位都是相对于 div 发生的,这意味着它不可避免地会位于整个图表下方,并且不在上面:

  // create a tooltip
  var tooltip = d3
    // Select all boxes in the chart
    .select("#chart2")
    .append("div")
    .classed("tooltip", true)
    .style("opacity", 0)
    .attr("class", "tooltip")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "2px")
    .style("border-radius", "5px")
    .style("padding", "5px");

const mousemove = function(event, d) {
        tooltip
          .html("The exact value of<br>this cell is: " + d.value)
          // Position the tooltip next to the cursor
          .style("left", event.x + "px")
          .style("top", event.y / 2 + "px");
      };

但是我还能怎么做呢?我尝试通过悬停在 rect 元素上的 select 尝试,但没有奏效。我正在尝试做类似 this.

的事情

我该如何解决这个问题?

A live version can be found here

只需在 mousemove 函数中添加 .style("position","absolute"); 并将顶部样式更改为 .style("top", event.y + "px")

const margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 150,
};
const width = 960 - margin.left - margin.right;
const height = 2500 - margin.top - margin.bottom;

let chart = d3
  .select("#chart2")
  .append("div")
  // Set id to chartArea
  .attr("id", "chartArea")
  .classed("chart", true)
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Make sure to create a separate SVG for the XAxis
let axis = d3
  .select("#chart2")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", 40)
  .append("g")
  .attr("transform", "translate(" + margin.left + ", 0)");

// Load the data
d3.csv("https://raw.githubusercontent.com/thedivtagguy/files/main/land_use.csv").then(function(data) {
  // console.log(data);
  const years = Array.from(new Set(data.map((d) => d.year)));
  const countries = Array.from(new Set(data.map((d) => d.entity)));
  countries.reverse();
  const x = d3.scaleBand().range([0, width]).domain(years).padding(0.01);

  // create a tooltip
  var tooltip = d3
    // Select all boxes in the chart
    .select("#chart2")
    .append("div")
    .classed("tooltip", true)
    .style("opacity", 0)
    .attr("class", "tooltip")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "2px")
    .style("border-radius", "5px")
    .style("padding", "5px");

  const mouseover = function(event, d) {
    tooltip.style("opacity", 1);
    d3.select(this).style("stroke", "black").style("opacity", 1);
  };

  const mousemove = function(event, d) {
    tooltip
      .html("The exact value of<br>this cell is: " + d.value)
      // Position the tooltip next to the cursor
      .style("left", event.x + "px")
      .style("top", event.y + "px")
      .style("position","absolute");
  };

  const mouseleave = function(event, d) {
    tooltip.style("opacity", 0);
    d3.select(this).style("stroke", "none").style("opacity", 0.8);
  };

  const y = d3.scaleBand().range([height, 0]).domain(countries).padding(0.01);

  // Only 10 years
  axis
    .call(d3.axisBottom(x).tickValues(years.filter((d, i) => !(i % 10))))
    .selectAll("text")
    .style("color", "black")
    .style("position", "fixed")
    .attr("transform", "translate(-10,10)rotate(-45)")
    .style("text-anchor", "end");

  chart
    .append("g")
    .call(d3.axisLeft(y))
    .selectAll("text")
    .style("color", "black")
    .attr("transform", "translate(-10,0)")
    .style("text-anchor", "end");

  const colorScale = d3
    .scaleSequential()
    .domain([0, d3.max(data, (d) => d.change)])
    .interpolator(d3.interpolateInferno);

  // add the squares
  chart
    .selectAll()
    .data(data, function(d) {
      return d.year + ":" + d.entity;
    })
    .join("rect")
    // Add id-s
    .attr("id", function(d) {
      return d.year + ":" + d.entity;
    })
    .attr("x", function(d) {
      return x(d.year);
    })
    .attr("y", function(d) {
      return y(d.entity);
    })
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function(d) {
      return colorScale(d.change);
      console.log(d.change);
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8)
    .on("mouseover", mouseover)
    .on("mousemove", mousemove)
    .on("mouseleave", mouseleave);
});
#chart2 .chart {
  width: 960px;
  max-height: 900px;
  overflow-y: scroll;
  overflow-x: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<div id="chart2" class="mx-auto"></div>