score:1
There are at least a few things going on here. As pointed out by Michael Rovinsky, you're appending a new g
group for every update that houses all of your bars. Since it's a new group, it has no data bound to it, which will make new bars for every call.
Keeping track of groups
Instead, you should make the group only once, and maybe assign it to a variable to make it a little clearer to work with. I'll use the this.svg.append("g").attr("class", "bars");
group since it doesn't seem to be used for anything else:
var bars = this.svg.append("g").attr("class", "bars");
Then in your update function, change:
this.svg
.append("g")
.selectAll("g")
// ...
to
bars
.selectAll("g")
Returning values for the join selection
Your invocations of join
aren't quite standard when you don't return the enter
, update
, and exit
selections. When you have an arrow function with a body like:
enter => {
enter.append('g') //...
}
it doesn't actually return the enter selection, it should be either
enter => {
return enter.append('g') //...
}
or
enter => enter.append('g') //...
While you don't technically need to return the enter
or update
selections, it's the intended way to use them, and does cause problems when you try to use the combined enter
and update
selections later, which we will later do.
Understanding the update process
The way you implement your nested joining suggests that you're not quite understanding how joining works. When you enter
and append new elements, those elements don't have any data bound to them, so when you use a subsequent join
, the nested update
and exit
functions won't ever run.
You'll also notice that you're repeating a lot of the same code in both enter
and update
.
The more standard way of doing what I think you intend to do, is to add new bars if necessary, and then to use the new bars and old bars (which is returned by join
and update them with the bar segments.
It'll look something like this:
bars.selectAll('g').data(data).join(
enter => enter.append('g'),
update => update, // don't do anything with only the update selection
exit => exit.remove()
) // now we're selecting all of the enter and update g's
.selectAll('rect')
.data(d => d).join(
enter => enter.append('rect'),
update => update,
exit => exit.remove()
) // now we're selecting all of the enter and update rects
.transition(t)
// ...
Setting Keys
If you look at the format of the data you can see that stackData is always an array of length 3 that has an array for each entry. Those nested arrays vary based on the number of columns and it's at that level that the keys should be defined:
//... group selection
.selectAll('rect')
.data(d => d, d => d.data.key)
//...
Fixing Bugs
Finally there's bit of code when you're updating the bar location .attr("x", d => this.xScale(d.key))
which should use d.data.key
instead.
All together
I consolidated most of the redundant enter and update code, and removed some extraneous code. All together, it looks like:
this.width = 400;
this.height = 200;
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
}
this.index = 0;
this.svg = d3
.select(".canvas")
.classed("svg-container", true)
.append("svg")
.attr("class", "chart")
.attr(
"viewBox",
`0 0 ${this.width} ${this.height}`
)
.attr("preserveAspectRatio", "xMinYMin meet")
.classed("svg-content-responsive", true)
.append("g");
const scale = [0, 1200];
// set the scales
this.xScale = d3
.scaleBand()
.range([0, width])
.padding(0.3);
this.yScale = d3.scaleLinear().range([this.height, 0]);
var bars = this.svg.append("g").attr("class", "bars");
const update = data => {
const scale = [0, 1200];
// Update scales.
this.xScale.domain(data.map(d => d.key));
this.yScale.domain([scale[0], scale[1]]);
const subgroups = ["home", "work", "public"];
var color = d3
.scaleOrdinal()
.domain(subgroups)
.range(["#206BF3", "#171D2C", "#8B0000"]);
var stackData = d3.stack().keys(subgroups)(data);
// Set up transition.
const dur = 1000;
const t = d3.transition().duration(dur);
bars
.selectAll("g")
.data(stackData)
.join(
enter => enter
.append("g")
.attr("fill", d => color(d.key)),
null, // no update function
exit => {
exit
.transition()
.duration(dur / 2)
.style("fill-opacity", 0)
.remove();
}
).selectAll("rect")
.data(d => d, d => d.data.key)
.join(
enter => enter
.append("rect")
.attr("class", "bar")
.attr("x", d => {
return this.xScale(d.data.key);
})
.attr("y", () => {
return this.yScale(0);
})
.attr("height", () => {
return this.height - this.yScale(0);
})
.attr("width", this.xScale.bandwidth())
,
null,
exit => {
exit
.transition()
.duration(dur / 2)
.style("fill-opacity", 0)
.remove();
}
)
.transition(t)
.delay((d, i) => i * 20)
.attr("x", d => this.xScale(d.data.key))
.attr("y", d => {
return this.yScale(d[1]);
})
.attr("width", this.xScale.bandwidth())
.attr("height", d => {
return this.yScale(d[0]) - this.yScale(d[1]);
});
};
const data = [
[{
key: "Jan",
home: 371,
work: 335,
public: 300
},
{
key: "Feb",
home: 343,
work: 437,
public: 228
},
{
key: "Mrt",
home: 359,
work: 261,
public: 202
},
{
key: "Apr",
home: 274,
work: 217,
public: 482
},
{
key: "Mei",
home: 442,
work: 314,
public: 477
},
{
key: "Jun",
home: 464,
work: 261,
public: 278
},
{
key: "Jul",
home: 343,
work: 244,
public: 396
},
{
key: "Aug",
home: 231,
work: 406,
public: 338
},
{
key: "Sep",
home: 380,
work: 382,
public: 366
},
{
key: "Okt",
home: 391,
work: 408,
public: 455
},
{
key: "Nov",
home: 419,
work: 261,
public: 226
},
{
key: "Dec",
home: 217,
work: 453,
public: 335
}
],
[{
key: "Jan",
home: 282,
work: 363,
public: 379
},
{
key: "Feb",
home: 428,
work: 355,
public: 216
},
{
key: "Mrt",
home: 217,
work: 493,
public: 280
},
{
key: "Apr",
home: 304,
work: 283,
public: 454
},
{
key: "Mei",
home: 397,
work: 406,
public: 289
},
{
key: "Jun",
home: 242,
work: 239,
public: 232
},
{
key: "Jul",
home: 327,
work: 453,
public: 264
},
{
key: "Aug",
home: 242,
work: 240,
public: 414
},
{
key: "Sep",
home: 495,
work: 382,
public: 368
},
{
key: "Okt",
home: 285,
work: 471,
public: 364
},
{
key: "Nov",
home: 315,
work: 421,
public: 482
},
{
key: "Dec",
home: 214,
work: 284,
public: 297
}
],
[{
key: "1",
home: 282,
work: 363,
public: 379
},
{
key: "2",
home: 232,
work: 432,
public: 324
},
{
key: "3",
home: 324,
work: 124,
public: 432
},
{
key: "4",
home: 425,
work: 353,
public: 532
}
]
];
update(data[this.index]);
const swap = document.querySelector(".swap");
swap.addEventListener("click", () => {
if (this.index < 2) this.index += 1;
else this.index = 0;
update(data[this.index]);
});
<button class="swap">swap</button>
<div class="canvas"></div>
<script src="https://d3js.org/d3.v6.js"></script>
score:0
The stateData you pass to .selectAll('g').data(stackData) is an array of arrays and does not contain keys. You need to enclose every array into an object and specify unique key:
correctStackData = stackData.map((item, index) => ({data: item, key: index}));
score:0
Exit will not work because you add a "g" container on each update. Just add the following line:
this.svg.selectAll("g").transition().duration(dur).style("opacity",0).remove();
before you call
this.svg.append("g").selectAll("g").data(stackData...
You can see the solution working in a fiddle
Source: stackoverflow.com
Related Query
- D3.js - How to add the general update pattern to a stacked bar chart
- Adapting diverging stacked bar chart to use general update pattern
- Stacked Bar Chart with general update pattern d3.js
- D3 - General Update Pattern in stacked bar chart
- How to modify axis labels in d3 for a stacked bar chart when the axis labels are mapped as part of the scale's domain
- D3: How to do an update pattern on normalized stacked bar chart?
- How to add live data to stacked bar chart
- D3.js line chart using the wrong keys when using general update pattern
- How do I set the X values for a singled stacked horizontal bar chart using d3.js?
- How can I make the legend's similar to the stacked bar chart (and change the legend's label) in D3 v6?
- How to display the total value of a stacked bar chart in d3js
- how to update text added in dimple.js stacked bar chart on data updation
- How to display total count on top of the stacked bar chart
- D3 How to add rounded top border to a stacked bar chart
- How to add a line at top of stacked bar chart with d3.js?
- D3.js Line chart not updating properly when applying the general update pattern
- d3js - Stacked Bar Chart in Horizontal Axis - How do I set x so the bars are seperated?
- How do I manage groups for a grouped bar chart with a dropdown to update the chart in D3?
- How to plot stacked bar chart according to the custom Y axis in D3
- How to make axis label of stacked bar chart in the middle of ticks chart in D3?
- How to add a properly scaled y axis to a stacked d3 bar chart
- How to update d3.js bar chart with new data
- d3.js How to add lines to a bar chart
- D3 update dataset on click and redraw the bar chart
- how to add legend to a pie chart using D3js? And how to centralise the pie chart?
- How to add a line on x-axis on a horizontal bar chart in d3
- How to add space between bars in a grouped bar chart in a nvd3 grouped multibar chart?
- D3.js: how to follow general update pattern to transition nested elements?
- How do I implement d3's enter update exit pattern for an area chart with focus and context?
- How to update data in stack bar chart in D3
More Query from same tag
- How to add on-click events to D3 axis ticks
- Dynamically changing the Javascript D3 layout simulation
- What are unit testing strategies for D3JS?
- Getting text area using getBBox()
- d3.js (v4) Scatter w/ Tooltip
- d3.js Series Circle chart
- Get svg image real size after resizing
- D3.js conflict with Boostrap
- Zoom with Slider and mouse scroll in d3.js
- plotting json data in phant using d3 js
- Processing a multidimensional data array with d3.js
- Why doesn't click event always fire?
- How to make the NVD3 discreteBarChart labels on the X axis to adapt to the width or to the number of labels?
- d3 line chart coloured by a factor
- D3 nested objects should be different colors
- understanding dc.js charts interactivity better with fiddle example
- How to apply drag behavior to a d3.svg.arc?
- D3.js v6.2 - get data index in listener function - selection.on('click', listener)
- How do I create the update function in D3JS
- How do I brush chart nodes in a D3 SVG?
- D3.js - How to add the general update pattern to a stacked bar chart
- Turn off clipping in a d3 projection
- Django: create csv file and load it in view using Javascript
- Convertion from CSV to JSON in d3.js
- D3plus options having no effect on chart
- I've defined a path in d3.js, it draws correctly, but .getTotalLength() is undefined
- D3 Line chart - display value label on Y axis tick and a not scale
- add space/gap in a line in D3
- Cannot display tooltip in bubble chart with d3.js
- Unable to visualize label in donut chart - D3