Search code examples
d3.jsaxes

Behavior of chaining syntax in axes generation


I'm writing d3.js (d3 v4) code to generate a simple bar chart, which I managed to do, but I am confused about the behavior of the code in generating axes.

So basically, I encapsulated the chart code into a function that contains basic accessors.

Here is some code showing the core of my chart function.

function chart(selection){
      selection.each(function(data){

        var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.5);
        var y = d3.scale.linear().range([height, 0]);

        var xAxis = d3.svg.axis()
          .scale(x)
          .orient("bottom");

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

        x.domain(data.map(function(d) { return d.name; }));
        y.domain([0, d3.max(data, function(d) { return d.value; })]);

        var div = d3.select(this)
        var svg = div.selectAll("svg")
          .data([data]).enter()
          .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 + ")");

        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", "-.55em")
          .attr("transform", "rotate(-90)" );

        svg.append("g")
          .attr("class", "y axis")
          .call(yAxis)
          .append("text")
          .attr("transform", "rotate(-90)")
          .attr("y", 5)
          .attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("Frequency");

        svg.selectAll(".bar")
          .data(data)
          .enter().append("rect")
          .attr("class", "bar")
          .attr("fill", "#1CE6FF")
          .attr("x", function(d) { return x(d.name); })
          .attr("width", x.rangeBand())
          .attr("y", function(d) { return y(d.value); })
          .attr("height", function(d) { return height - y(d.value); });
      });

So I have two questions: 1) Is it okay to use multiple code blocks invoking svg.append("g") like shown above in the code? 2) When I replace the following code :

var svg = div.selectAll("svg")
          .data([data]).enter()
          .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 + ")");

        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", "-.55em")
          .attr("transform", "rotate(-90)" );

With this (which is basically merging the two blocks in a single chain code chunk):

var svg = div.selectAll("svg")
          .data([data]).enter()
          .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 + ")").append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis)
          .selectAll("text")
          .style("text-anchor", "end")
          .attr("dx", "-.8em")
          .attr("dy", "-.55em")
          .attr("transform", "rotate(-90)" );

...why is my y axis not showing at the right place? The <g class="y axis">...</g> tag appears nested into of a <text> tag itself nested in the x axis tag. Why the 2-block code vs single block code not behaving the same?


Solution

  • The structure of the code defines the structure of the resulting SVG. In the function chart() the variable svg holds the reference to the lastly appended <g> not to the previously appended <svg> itself. Note, how all statements after its first creation append to that same outer group of svg. That's one perfectly fine way of doing this.

    If you concatenate the statements, however, like you did in your last snippet, the value of svg changes because the right hand side, i.e. the return value of the expression, changes. Since you last selected all text elements of the appended x-axis svg contains exactly those texts. Given that, it is pretty easy to understand that this breaks the rest of the code which will now append other contents to the text elements. Even if it was syntactically valid, which it is not, it would most likely still break the layout.

    If you want to rearrange the code structure you have to make sure to have the correct references at the time they are needed.