求助: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 processundefinedvalues, 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()ord3.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
scaleBandto 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:
yuses the y scale to fix the bar’s vertical positionheightusesyScale.bandwidth()for the bar’s heightxis the start position of the segment (fromxScale(d[0][0]))widthis 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—ifwidthis 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 theirx,y,width, andheightare set to reasonable numbers (no zeros or negative values!).
内容的提问来源于stack exchange,提问作者rainbowunicorn
相关产品推荐
相关产品推荐

