1

I have designed a chart using d3.js and have embedded it in a div within a UIkit framework, however I can't get it to centre within the div.

I have tried putting the svg in a container and adjusting the CSS by putting the svg in a flexbox, removing the margins, aligning it centre, but none of it works. I have tried this response, this one, this one and this one and none of them work. I'm sure it must be something simple - how can I get it to centre? Code below.

Chart

<div id="age_chart" class="uk-container uk-width-medium-1-2 uk-container-center uk-margin-top">
  <script>
  // chart based on this example: https://bl.ocks.org/63anp3ca/6bafeb64181d87750dbdba78f8678715
  // var svg1 = d3.select("svg1"),
  var margin = {top: 20, right: 20, bottom: 100, left: 20},
      width1 = d3.select("#age_chart").node().getBoundingClientRect().width,
      height1 = 400 - margin.top - margin.bottom;
      // g = svg1.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // The scale spacing the groups:
  var x0 = d3.scaleBand()
      .rangeRound([0, width1])
      .paddingInner(0.05);

  // The scale for spacing each group's bar:
  var x1 = d3.scaleBand()
      .padding(0.05);

  var y = d3.scaleLinear()
      .rangeRound([height1, 0]);

  var z = d3.scaleOrdinal()
      .range(["#2c7fb8", "#7fcdbb"]);

    var svg1 = d3.select('body').append("svg")
    .attr("width", width1 + margin.left + margin.right)
    .attr("height", height1 + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  d3.csv("data/age_distribution.csv", function(d, i, columns) {
      for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
      return d;
  }).then(function(data) {
      console.log(data);

      var keys = data.columns.slice(1);

      console.log('keys');
      console.log(keys);
      x0.domain(data.map(function(d) { return d.age; }));
      x1.domain(keys).rangeRound([0, x0.bandwidth()]);
      y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();

      svg1.append("g")
          .selectAll("g")
          .data(data)
          .enter().append("g")
          .attr("class","bar")
          .attr("transform", function(d) { return "translate(" + x0(d.age) + ",0)"; })
          .selectAll("rect")
          .data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
          .enter().append("rect")
          .attr("x", function(d) { return x1(d.key); })
          .attr("y", function(d) { return y(d.value); })
          .attr("width", x1.bandwidth())
          .attr("height", function(d) { return height1 - y(d.value); })
          .attr("fill", function(d) { return z(d.key); });

      svg1.append("g")
          .attr("class", "axis")
          .attr("transform", "translate(0," + height1 + ")")
          .call(d3.axisBottom(x0));

      svg1.append("g")
          .attr("class", "y axis")
          .call(d3.axisLeft(y).ticks(null, "s"))
          .append("text")
          .attr("x", 2)
          .attr("y", y(y.ticks().pop()) + 0.5)
          .attr("dy", "0.32em")
          .attr("fill", "#000")
          .attr("font-size", 11)
          .attr("text-anchor", "start")
          .text("Percentage of population")
          .attr("font-family", "Archivo");

      var legend1 = svg1.append("g")
          .attr("font-family", "inherit")
          .attr("font-size", 11)
          .attr("text-anchor", "end")
          .selectAll("g")
          .data(keys.slice().reverse())
          .enter().append("g")
          .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

      legend1.append("rect")
          .attr("x", width1 * .9)
          .attr("width", 15)
          .attr("height", 15)
          .attr("fill", z)
          .attr("stroke", z)
          .attr("stroke-width",2)
          .on("click",function(d) { update(d) });

      legend1.append("text")
          .attr("x", width1 * .85)
          .attr("y", 9.5)
          .attr("dy", "0.32em")
          .text(function(d) { return d; });

      var filtered = [];

      ////
      //// Update and transition on click:
      ////

      function update(d) {

          //
          // Update the array to filter the chart by:
          //

          // add the clicked key if not included:
          if (filtered.indexOf(d) == -1) {
              filtered.push(d);
              // if all bars are un-checked, reset:
              if(filtered.length == keys.length) filtered = [];
          }
          // otherwise remove it:
          else {
              filtered.splice(filtered.indexOf(d), 1);
          }

          //
          // Update the scales for each group's items:
          //
          var newKeys = [];
          keys.forEach(function(d) {
              if (filtered.indexOf(d) == -1 ) {
                  newKeys.push(d);
              }
          })
          x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
          y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { if (filtered.indexOf(key) == -1) return d[key]; }); })]).nice();

          // update the y axis:
          svg1.select(".y")
              .transition()
              .call(d3.axisLeft(y).ticks(null, "s"))
              .duration(500);


          //
          // Filter out the bands that need to be hidden:
          //
          var bars = svg1.selectAll(".bar").selectAll("rect")
              .data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })

          bars.filter(function(d) {
                  return filtered.indexOf(d.key) > -1;
              })
              .transition()
              .attr("x", function(d) {
                  return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width"))/2;
              })
              .attr("height",0)
              .attr("width",0)
              .attr("y", function(d) { return height1; })
              .duration(500);

          //
          // Adjust the remaining bars:
          //
          bars.filter(function(d) {
                  return filtered.indexOf(d.key) == -1;
              })
              .transition()
              .attr("x", function(d) { return x1(d.key); })
              .attr("y", function(d) { return y(d.value); })
              .attr("height", function(d) { return height1 - y(d.value); })
              .attr("width", x1.bandwidth())
              .attr("fill", function(d) { return z(d.key); })
              .duration(500);


          // update legend:
          legend1.selectAll("rect")
              .transition()
              .attr("fill",function(d) {
                  if (filtered.length) {
                      if (filtered.indexOf(d) == -1) {
                          return z(d);
                      }
                      else {
                          return "white";
                      }
                  }
                  else {
                      return z(d);
                  }
              })
              .duration(100);
      }

  });
  </script>
  </div>
paticake
  • 13
  • 5

1 Answers1

0

The problem is that the SVG isn't in your div.

The position of a script element doesn't decide where the SVG will get appended on the page. If you put it at the bottom of the page, you'll get the same results - you need to do so to wait for the DOM to be fully loaded.

d3.select('body').append("svg") will append the svg directly on the body element. To append it to the element you've created, since it has id age_chart, use:

d3.select('#age_chart').append("svg")
xy2
  • 6,040
  • 3
  • 14
  • 34
  • 1
    That has centred it, thank you! But the legend has now disappeared, do you know what might have caused that? Nothing's coming up when I debug it – paticake Aug 10 '20 at 10:59