D3.js Bar Chart won't append, creates a new chart

Viewed 407

I have been using d3.js to create a bar chart from a table of results. All the code is below. One table gets the data from the csv file. Then clones it to another table, when this happens I want the bar chart bars to change. But a new bar chart is created instead. Any advice or help would be really appreciated, thanks.

updateCode()
function test()
{
var source = document.getElementById('table');
var destination = document.getElementById('myclonediv');
var copy = source.cloneNode(true);
copy.setAttribute('id', 'myclonediv');
destination.parentNode.replaceChild(copy, destination);
var canvas;
createGraph()
}
function createGraph()
{
    d3.csv(googlesheet, function(data) {


        //create container - canvas
        canvas = d3.select("body").append("svg")
            //width and height
            .attr("width", 300)
            .attr("height", 1000)

        //add bars  - rectangle 
        canvas.selectAll("rect")
            //loading data variable
            .data(data)
            //enter method
            .enter()
                //append rectangle for each element
                .append("rect")
                //width - d will reference data, d.age - from csv file * 10 so bars are bigger
                .attr("width", function(d) { return d.Random * 10})
                //was 50 now 48 to allow space between bars
                .attr("height", 48)
                // y function of the index, for each element
                .attr("y", function(d, i) { return i *50})
                //colour blue
                .attr("fill", "blue");

            //var newData = parsedCSV.replace(',', '');
            //var test = parsedCSV.substring(parsedCSV.indexOf(",") + 1);


            //add text to each bar - repeat before
        canvas.selectAll("text")
            .data(data)
            .enter()
                .append("text")
                    //white text
                .attr("fill", "red")
                    //same position on each bar -- + 24 is half of 48 in height -- now middle of bar
                .attr("y", function(d,i) { return i *50+24})
                    //what text - d.name
                .text(function(d) { return d.Random; }) 

            }) 


}
function remove()
{
    var tableCode = d3.select("#table");
tableCode.selectAll("*").remove();
    updateCode()
}


function updateCode()
{
  var tabulate = function (data,columns) {                      
   var table = d3.select('#table').append('table')
    var thead = table.append('thead')
    var tbody = table.append('tbody')

    thead.append('tr')
      .selectAll('th')
        .data(columns)
        .enter()
      .append('th')
        .text(function (d) { return d })

    var rows = tbody.selectAll('tr')
        .data(data)
        .enter()
      .append('tr')

    var cells = rows.selectAll('td')
        .data(function(row) {
            return columns.map(function (column) {
                return { column: column, value: row[column] }
          })
      })
      .enter()
    .append('td')
      .text(function (d) { return d.value })

  return table;
}

d3.csv(googlesheet,function (data) {
    var columns = ['Test','Random']
  tabulate(data,columns)



})
}
1 Answers

Every time you run the createGraph() function, you do this:

canvas = d3.select("body").append("svg")

Which means your "canvas" selection is of a new svg element each time you run the function. With this new svg you select all rectangles and do an enter selection:

canvas.selectAll("rect")
    //loading data variable
    .data(data)
    //enter method
    .enter()

But, as there are no rectangles in the new svg, the enter selection creates a rectangle for each element. Same for text. And since it's a new svg, you've duplicated your graph.

(The enter selection will create a DOM element for each item in the data array that has no corresponding DOM element, your selection is empty, so it creates one element for each item in the array.)

The easiest fix would be to not append the svg in the createGraph function - append it in the html or before the function is called. Then in the createGraph function just select it:

svg = d3.select("svg") // assuming you have only one svg, otherwise give it an id or class

Now you have to execute a proper enter/update/exit cycle, but it's not clear what version of d3 you are using which is important because are differences between v3 and v4.

Related