Hoppertron
Hoppertron

Reputation: 166

Why doesn't this D3 chart redraw when I repeatedly call it's original draw function?

I'm trying to create a dashboard with resizable charts using the D3.js library. To do this I'm creating the chart SVG elements within DIVs, and those DIVs are made resizable using the jQuery-ui .resizable class.

The DIV's resized event is monitored by another library (css-element-queries) and fires per-pixel, which should call my drawGraph() function and redraw the graph in proportion to it's parent DIV, but this doesn't happen and I'm not sure why. Am I doing something stupid?

See this fiddle: https://jsfiddle.net/Hoppertron/ymf90p6v/17/

By the way, I know that in production I would want to throttle this redraw event - I'm just trying to get it working first.

Here's the code:

<body>
  <div id="div1" class="container resizable draggable">
    <svg id="chart1" class="chart"></svg>

    <script>
      //Watches for div resize event (using ResizeSensor.js) and calls drawGraph() to redraw SVG
      var element = document.getElementById('div1');
      new ResizeSensor(element, function(element) {
        var chartWidth = $("#div1").width(),
          chartHeight = $("#div1").height();
        drawGraph(chartWidth, chartHeight)
      });

    </script>
  </div>

  <script>
    //Calls drawGraph() on initial page load
    var chartWidth = $("#div1").width(),
      chartHeight = $("#div1").height();
    $(function() {
      drawGraph(chartWidth, chartHeight);
    });

  </script>

</body>

<script>
  function drawGraph(width, height) {

    var data = [4, 8, 15, 16, 23, 42];

    var barHeight = height / data.length;

    var x = d3.scale.linear()
      .domain([0, d3.max(data)])
      .range([0, width]);

    var chart = d3.select(".chart")
      .attr("width", width)
      .attr("height", height);

    var bar = chart.selectAll("g")
      .data(data)
      .enter().append("g")
      .attr("transform", function(d, i) {
        return "translate(0," + i * barHeight + ")";
      });

    bar.append("rect")
      .attr("width", x)
      .attr("height", barHeight - 1);

    bar.append("text")
      .attr("x", function(d) {
        return x(d) - 3;
      })
      .attr("y", barHeight / 2)
      .attr("dy", ".35em")
      .text(function(d) {
        return d;
      });
  };

</script>

Upvotes: 0

Views: 1577

Answers (2)

ocket-san
ocket-san

Reputation: 884

This might sound stupid, but i think you need to destroy the original graph first before redrawing it.

When you do a d3.selectAll(element), in the background, d3 is going to check the amount of elements vs the number of data elements. Then when you do enter(), d3 looks if there are data items that don't have a matching element yet and draws those elements when you do .append(). In your case, the data doesn't change, only the size of the graph. So nothing will get redrawn.

Upvotes: 1

KWeiss
KWeiss

Reputation: 3144

You need to clear the chart before redrawing it. Add this line at the start of your drawGraph function:

d3.selectAll(".chart > *").remove();

Fiddle here: https://jsfiddle.net/ymf90p6v/19/

If you don't clear it, the data remains attached to the existing elements and no new elements are created.

Upvotes: 1

Related Questions