You have to empty the barchart first:

let svg ='#barChart')

Not sure if this is the most effective way, but that's up to you :)


Every time you run this function you create a new svg:

let svg ='#barChart').append('svg')

This newly appended svg will be appended after the last svg you created. You need to select the existing svg if it exists, one easy way to do this is to manually add the svg and manipulate it:

<div id="barChart"><svg></svg></div>

let svg ="#barChart").select("svg");

There are other ways of course too (check to see if .select("svg") is empty, even use an enter/update cycle for example).

This introduces a new problem though, your code doesn't use an update selection, it only works with enter selections, but if you break up your chaining you can access both the enter and the update, there is a lot of material on this so I won't go into the details (the key issue is that .enter() returns a new selection, but since you only enter elements the first time it is empty every other time your run the function). Keep in mind d3v3 has a different update/enter pattern than d3v4/5 if looking at references. Correcting for this, we might get something like the below snippet (click to update):


function t() {

    let obj = {
      0: 'Bar 1',
      1: 'Bar 2',
      2: 'Bar 3',
      3: 'Bar 4',
      4: 'Bar 5',
      5: 'Bar 6',

    let data = [
      { xAxis: 0, value: Math.random()*10 },
      { xAxis: 1, value: Math.random()*11 },
      { xAxis: 2, value: Math.random()*12 },
      { xAxis: 3, value: Math.random()*12 },
      { xAxis: 4, value: Math.random()*11 },
      { xAxis: 5, value: Math.random()*10 },
    let margin = {
      top: 20,
      right: 160,
      bottom: 35,
      left: 0
    let width = 700;
    let height = window.innerHeight / 1.8;
    let fromLeft = 40;

    let svg ='#barChart')
    svg.attr('width', width + 100)
      .attr('height', height + + margin.bottom + 2)
      .attr('transform', 'translate(' + (margin.left + fromLeft) + ',' + + ')');

    let dataset = d3.layout.stack()(['value'].map((value) => {
      return => {
        return { x: obj[d.xAxis], y: d.value };

    let x = d3.scale.ordinal()
      .domain(dataset[0].map((d) => { return d.x; }))
      .rangeBands([0, width], 1);

    let y = d3.scale.linear()
      .domain([0, d3.max(dataset, (d) => {
        return d3.max(d, (d1) => { return d1.y0 + d1.y; });
      .range([height, 0]);
    let colors = ['#169fcd', '#f26722'];

    let yAxis = d3.svg.axis()
      .tickSize(-width, 0, 0)

    let xAxis = d3.svg.axis()

      .attr('class', 'y axis')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0,' + height + ')')


      .style('display', 'none');

    let groups = svg.selectAll('g.cost')
      .attr('class', 'cost')
      .style('fill', (d, i) => { return colors[i]; });

    let rect = groups.selectAll('rect')
      .data((d) => { return d; });
      .attr('x', (d) => { return x(d.x); })
      .attr('y', height - 1)
      .attr('height', 0)
      .attr('width', '11px')
      .attr('y', (d) => {return y(d.y0 + d.y); })
      .attr('height', (d) => { return y(d.y0) - y(d.y0 + d.y); });

<script src=""></script>

<div id="barChart"><svg></svg></div>
<script src=""></script>

You'll also want to make sure you don't keep appending new labels each run too, just as with the svg

You can remove everything from the div as suggested in the other answer, but it eliminates the possibility of transitioning from one set of data to the next.

As for your attempts to use .exit().remove(), this will only work if your new data array has less elements than the previous data array (the excess elements will be in the exit selection), or if using a key for the bound data (if existing elements in the selection don't have matching keys in the new data array they will be exited).

Related Query

More Query from same tag