javascriptd3.jsselectall

javascript D3 V6, struggling with chained syntax, specifically effects of '.call' and 'selectAll'


Below are two examples of similar D3 code, one works and the other doesn't. In this example, I want to change the color of the lines of an axis -

This doesn't work, stroke color lines of the axis do not get changed to green -

   var x_axis = svg.append("g")
      .attr("class", "axis")
      .attr("transform", `translate(20, ${height - 50})`)
      .call(d3.axisBottom(ordinalScale))
         .selectAll("text")
         .attr("transform", "translate(-5,5)rotate(-45)")
         .style("text-anchor", "end")
         .style("font-size", "8px")
         .style("fill", "#102040");

   x_axis.selectAll("line, path").style("stroke", "green");

BUT this works, the lines get changed to green:

   var x_axis = svg.append("g")
      .attr("class", "axis")
      .attr("transform", `translate(20, ${height - 50})`)
      .call(d3.axisBottom(ordinalScale));

   x_axis.selectAll("text")
      .attr("transform", "translate(-5,5)rotate(-45)")
      .style("text-anchor", "end")
      .style("font-size", "8px")
      .style("fill", "#102040");

   x_axis.selectAll("line, path").style("stroke", "green");

The difference being that in the first (failed) example, I chain the 'selectAll("text")' operations to the 'call(d3.axisBottom)' with the 'selectAll("line, path")' operations in a following expression, and in the second (successful) example, I have following seperate expressions for each of the text and line/path operations.

This is not critical since I can get the effect I want, but it seems to me that they should be equivalent but obviously there is some subtlety of the syntax that I do not understand. Does this have something to do with the '.call' operation?


Solution

  • The first code block doesn't work because x_axis doesn't contain what you think it does.

      var x_axis = svg.append("g")   // returns a selection of a g element
         .attr("class", "axis")      // returns the same selection of a g
         ...
         .call(d3.axisBottom(ordinalScale)) // returns the same selection of a g
         .selectAll("text")                 // returns a new selection of text elements
         ...                                
         .style("fill", "#102040");         // returns the same selection of text elements
    

    x_axis is defined by the last value returned by the chain. So, x_axis in the above is a selection of text elements, text elements can't (and in this case don't) contain any child path or line elements, so x_axis.selectAll('line, path') will return an empty selection. Consequently, setting any property for an empty selection won't change anything.

    The second code block works because x_axis remains a selection of a g - selection.call() returns the same selection that .call() was chained to, like .attr() or .style(), among other methods. Whereas selectAll() and select(), among other methods, return new selections.