3

So, I made a bar chart visualization for a webapp, and it continuously updates over time as new data is entered in.

The webapp is here.

As you can see, I am able to adjust the width of the bars as new data is entered in. This was the code I used to do that:

function updateBarChart(currentInfo) {
    var bars = d3.select("#bar-chart")
                 .selectAll(".bar")
                 .data(currentInfo)
                 .transition()
                 .style("width", function(d) { return String(xScale(d.prob)) + "px"; });

    var h2s = d3.select("#bar-chart")
                .selectAll("h2")
                .data(currentInfo)
                .transition()
                .text(function(d) { return d.title; });

    var h1s = d3.select("#bar-chart")
                .selectAll("h1")
                .data(currentInfo)
                .transition()
                .text(function(d) { return String(Math.round(d.prob * 100)) + "%"; });

    bars.enter().append("div")
        .attr("class", "bar")
        .transition()
        .style("width", function(d) { return String(xScale(d.prob)) + "px"; })

    bars.exit().remove()

    h2s.enter().append("h2")
       .transition()
       .text(function(d) { return d.title; })

    h2s.exit().remove()

    h1s.enter().append("h1")
       .transition()
       .text(function(d) { return String(Math.round(d.prob * 100)) + "%"; })

    h1s.exit().remove()

}

The input to the function is a list of dictionaries that have the form

{ title: 'title',
 prob: probability(float)
}

For some reason I keep getting the error "Uncaught TypeError: bars.enter is not a function". I've looked over the code, which was inspired mostly by this article on Medium.

Is there something wrong with my code here? I'm pretty new to d3 so I'm not entirely sure if I'm missing something important.

Any help is appreciated! Thanks!

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
pythontyler
  • 131
  • 3
  • 5

1 Answers1

1

Problem:

If you check the chaining method, your "enter" selection right now is equivalent to this:

var bars = d3.select("#bar-chart")
    .selectAll(".bar")
    .data(currentInfo)
    .transition()
    .style("width", function(d) { return String(xScale(d.prob)) + "px"; })
    .enter().append("div")
    .attr("class", "bar")
    .transition()
    .style("width", function(d) {
            return d + "px";
     })

Which will not work.

Solution:

Since the problem is that the bars variable should contain just the bound data, without the "update" part, this block:

 var bars = d3.select("#bar-chart")
     .selectAll(".bar")
     .data(currentInfo)
     .transition()
     .style("width", function(d) { return String(xScale(d.prob)) + "px"; });

Should be:

 var bars = d3.select("#bar-chart")
     .selectAll(".bar")
     .data(currentInfo);

 bars.transition()
     .style("width", function(d) { return String(xScale(d.prob)) + "px"; });

That way, your "enter" selection will be equivalent to this:

var bars = d3.select("#bar-chart")
     .selectAll(".bar")
     .data(currentInfo)
     .enter().append("div")
     .attr("class", "bar")
     .transition()
     .style("width", function(d) {
            return d + "px";
      })

Here is a demo using (parts of) your code:

setInterval(function() {
    var data = d3.range(~~(Math.random() * 20)+1)
      .map(()=>~~(Math.random()*300));
    updateBarChart(data);
}, 2000);



function updateBarChart(currentInfo) {
    var bars = d3.select("body")
        .selectAll(".bar")
        .data(currentInfo);

    bars.transition()
        .style("width", function(d) {
            return d + "px";
        })
        .text(function(d) { 
            return d; 
        });

    bars.enter().append("div")
        .attr("class", "bar")
        .transition()
        .style("width", function(d) {
            return d + "px";
        })
        .text(function(d) { 
            return d; 
        });

    bars.exit().remove();

}
.bar {
  background-color: steelblue;
  text-align: right;
  padding: 3px;
  margin: 1px;
  color: white;
  font: 10px sans-serif;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171