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

求助:D3.js创建水平单一堆叠条形图失败,rect元素未显示

Hey there! Let's figure out why your horizontal single stacked bar isn't rendering those <rect> elements and squash that TypeError at the same time. I’ve built a few of these "bar chart pie" visualizations before, so let’s break down the common pitfalls:

1. First, Fix That TypeError (slice() on Undefined/Null)

That error almost always points to a mismatch between your data structure and how D3’s stack() function expects to process it. Here’s what to check:

  • Double-check your stack().keys(): Make sure the keys you’re passing exactly match the property names in your data objects (case-sensitive!). If a key doesn’t exist in your data, D3 will try to process undefined values, which triggers the slice error.
  • Validate your input data: Ensure you’re passing an array of objects to stack(), not a single object or null. For a single stacked bar, your data should look something like this:
    const data = [{ segmentA: 25, segmentB: 40, segmentC: 35 }]; // One "row" for the whole bar
    
  • Wait for data loading: If you’re pulling data from an external source (CSV/JSON), make sure you run your stacking logic inside the d3.csv() or d3.json() callback—don’t try to process data that hasn’t loaded yet!

2. Why Your <rect> Elements Are Missing

Even if the error is fixed, horizontal stacked bars have a few key differences from vertical ones that can hide your rects:

  • You mixed up x/y scales: For horizontal bars, your x scale handles the numerical values (width of each segment), and your y scale positions the single bar vertically. Since you removed the y axis, you still need a scaleBand to define the bar’s position and height:
    const yScale = d3.scaleBand()
      .domain(["single-bar"]) // A single category for your one bar
      .range([margin.top, margin.top + barHeight]) // Set vertical position
      .padding(0); // No gaps needed for a single bar
    
  • Incorrect rect attributes: When drawing each segment, make sure you’re using the right scale for each attribute:
    • y uses the y scale to fix the bar’s vertical position
    • height uses yScale.bandwidth() for the bar’s height
    • x is the start position of the segment (from xScale(d[0][0]))
    • width is the difference between the segment’s end and start positions (xScale(d[0][1]) - xScale(d[0][0]))
  • Check for zero-width segments: Use your browser’s dev tools to inspect the <rect> elements—if width is 0, your x scale’s domain might be wrong. Verify it covers the total sum of your segments with:
    const xScale = d3.scaleLinear()
      .domain([0, d3.max(stackedData, d => d3.max(d, seg => seg[1]))])
      .range([margin.left, width - margin.right]);
    

3. Working Example to Reference

Here’s a complete, minimal implementation you can adapt to your data:

<!DOCTYPE html>
<html>
<head>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
  <svg width="400" height="120"></svg>
  <script>
    const svg = d3.select("svg");
    const width = +svg.attr("width");
    const height = +svg.attr("height");
    const barHeight = 60;
    const margin = { top: 15, right: 20, bottom: 30, left: 20 };

    // Sample data for your single stacked bar
    const data = [{ red: 30, blue: 45, green: 25 }];

    // Configure the stack generator
    const stack = d3.stack().keys(["red", "blue", "green"]);
    const stackedData = stack(data);

    // Scales
    const xScale = d3.scaleLinear()
      .domain([0, d3.max(stackedData, d => d3.max(d, s => s[1]))])
      .range([margin.left, width - margin.right]);

    const yScale = d3.scaleBand()
      .domain(["main-bar"])
      .range([margin.top, margin.top + barHeight])
      .padding(0);

    // Color scale
    const color = d3.scaleOrdinal()
      .domain(["red", "blue", "green"])
      .range(["#e41a1c", "#377eb8", "#4daf4a"]);

    // Draw the stacked segments
    svg.selectAll("rect")
      .data(stackedData)
      .enter()
      .append("rect")
      .attr("y", () => yScale("main-bar"))
      .attr("height", yScale.bandwidth())
      .attr("x", d => xScale(d[0][0]))
      .attr("width", d => xScale(d[0][1]) - xScale(d[0][0]))
      .attr("fill", d => color(d.key));

    // Add a legend (since you removed the y axis)
    const legend = svg.selectAll(".legend")
      .data(stackedData)
      .enter()
      .append("g")
      .attr("class", "legend")
      .attr("transform", (d, i) => `translate(${margin.left}, ${margin.top + barHeight + 10 + i*18})`);

    legend.append("rect")
      .attr("width", 12)
      .attr("height", 12)
      .attr("fill", d => color(d.key));

    legend.append("text")
      .attr("x", 18)
      .attr("y", 10)
      .style("font-size", "12px")
      .text(d => d.key);
  </script>
</body>
</html>

Quick Debugging Tips

  • Use console.log(stackedData) to verify your stacked segments have valid [start, end] values.
  • Inspect the <rect> elements in your browser’s dev tools to check if their x, y, width, and height are set to reasonable numbers (no zeros or negative values!).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:03:51