1

A little background, I'm still fairly new to JS and development in general, so I may be missing something obvious. I got this chart working pretty well using d3, but I cannot get the positioning right no matter what I do. I've tried manipulating it with CSS and it just doesn't seem to behave in a logical way. I set display to block and margins to auto and it didn't affect it at all. The only way I can change the positioning is adjusting the margins in the d3 code, but that doesn't do very much for responsiveness. I've also tried using text-align and that didn't work either. What I'm trying to do is center it and have it scale larger as the screen size increases. This should all be easy to do in CSS in theory, but it just doesn't seem to work at all. Thanks for any help.

Here is the JS code:

            // Set the dimensions of the canvas / graph
            var margin = {top: 20, right: 0, bottom: 70, left: 70},
                width = 300 - margin.left - margin.right,
                height = 300 - margin.top - margin.bottom;

            // Parse the date / time
            var parseDate = d3.time.format("%-m/%-d/%Y").parse;

            // Set the ranges
            var x = d3.time.scale().range([0, width]);
            var y = d3.scale.linear().range([height, 0]);

            // Define the axes
            var xAxis = d3.svg.axis().scale(x)
                .orient("bottom").ticks(10);

            var yAxis = d3.svg.axis().scale(y)
                .orient("left").ticks(5);

            // chart area fill

            var area = d3.svg.area()
                .x(function(d) { return x(d.Date); })
                .y0(height)
                .y1(function(d) { return y(d.Forecast); });

            // Define the line
            var valueline = d3.svg.line()
                .interpolate("cardinal")
                .x(function(d) { return x(d.Date); })
                .y(function(d) { return y(d.Orders); });

            var valueline2 = d3.svg.line()
                .interpolate("cardinal")
                .x(function(d) { return x(d.Date); })
                .y(function(d) { return y(d.Forecast); });

            // Adds the svg canvas
            var svg = d3.select("body")
                .append("svg")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                .append("g")
                    .attr("transform", 
                          "translate(" + margin.left + "," + margin.top + ")");


            // Get the data
            d3.csv("csv/Forecast.csv", function(error, data) {
                data.forEach(function(d) {
                    d.Date = parseDate(d.Date);
                    d.Orders = +d.Orders;
                });
             // Scale the range of the data
                x.domain(d3.extent(data, function(d) { return d.Date; }));
                y.domain([0, d3.max(data, function(d) { return d.Orders; })]);


                // Area

                svg.append("path")
                    .datum(data)
                    .attr("class", "area")
                    .attr("d", area);

                // Add the valueline path.

                svg.append("path")
                    .attr("class", "line")
                    .attr("d", valueline2(data))
                    .style("stroke", "#A7A9A6");

                svg.append("path")
                    .attr("class", "line")
                    .attr("d", valueline(data));

                // Add the X Axis
                svg.append("g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0," + height + ")")
                    .call(xAxis)
                    .selectAll("text")
                        .style("text-anchor", "end")
                        .attr("dx", "-.8em")
                        .attr("dy", ".15em")
                        .attr("transform", "rotate(-65)");

                // Add the Y Axis
                svg.append("g")
                    .attr("class", "y axis")
                    .call(yAxis);

            });
trevor_bye
  • 31
  • 7

1 Answers1

0

JSFIDDLES

Responsive & Centered: https://jsfiddle.net/sladav/6fyjhmne/3/

Not Responsive: https://jsfiddle.net/sladav/7tp5vdkr/

For responsiveness, take advantage of the SVG viewBox:

Some links:

Setting up viewBox:

var margin = {top: 100, right: 150, bottom: 100, left: 150}

var outerWidth  = 1600,
    outerHeight = 900;

var width  = outerWidth - margin.right - margin.left,
    height = outerHeight - margin.top - margin.bottom;


d3.select(".plot-div").append("svg")
    .attr("class", "plot-svg")
    .attr("width", "100%")
    .attr("viewBox", "0 0 " + outerWidth + " " + outerHeight)
    .append("g")
      .attr("class", "plot-space")
      .attr("transform", 
        "translate(" + margin.left + "," + margin.top + ")"
      );

Points of note in the above:

  1. SVG is put in a div -- I'll adjust the size and position of the div rather than the svg
  2. SVG width is set as % of parent/div, not absolute.
  3. Everything you draw in SVG now is with respect to outerWidth x outerHeight and unitless parameters are rescaled by viewBox.

Taking a look at the rect in my JSFIDDLE example...

d3.select(".plot-svg").append("rect")
    .attr("x", 0)
    .attr("y", 3*outerHeight/4)
    .attr("width", 800)
    .attr("height", outerHeight/4)
    .attr("fill", "grey")

Resize your window and the rectangle will always fill 1/2 the svg because 800/1600 (note: 1600 is outerWidth).

For adjusting position/centering:

Manipulate the div containing your SVG/chart to position it how you want to. In my example it takes up 50% of the page and is centered because of margin: auto.

.plot-div{
  width: 50%;
  display: block;
  margin: auto;
 }

Whatever methods you use to scale/position your div, your chart should follow suit.

Community
  • 1
  • 1
Steve Ladavich
  • 3,472
  • 20
  • 27
  • Works perfectly now; including the responsive scaling. I made a noob mistake... I was using this: var svg = d3.select(".plot-div") instead of var svg = d3.select("div.plot-div"). I didn't realize you had to specifically call out the div and then the class name, which explains why my CSS was having no effect. Thanks! – trevor_bye Feb 20 '16 at 22:27
  • @trevor_bye, glad it's working. It's tough to say without seeing your code, but it sounds like you may be drawing an incorrect conclusion about having to call out the div first (which is not a general rule). The first class-only d3 selection should work fine in the case your css looks like: .plot-div{ } . If you were having an issue it might have been due to some incongruity in your d3 selection & css selectors where the specificity was a little off and you weren't applying css how you meant to (see here for more on specificity: https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity). – Steve Ladavich Feb 20 '16 at 22:49