rd3.jsr2d3

Scoping in r2d3 visualization - d3.selectAll vs svg.selectAll


Why does d3.selectAll('rect')... not work as seen in the script below in the mouseover function, but svg.selectAll('rect')... does?

svg is the special pre-set selector from r2d3.

Additionally, I have noticed that running e.g. d3.selectAll('rect').remove() from the browser console does not work when running the visualization from Rstudio.

This is from the r2d3 example, as sample.js:

// !preview r2d3 data=c(0.3, 0.6, 0.8, 0.95, 0.40, 0.20)

var barHeight = Math.floor(height / data.length);

svg.selectAll('rect')
  .data(data)
  .enter().append('rect')
    .attr('width', function(d) { return d * width; })
    .attr('height', barHeight)
    .attr('y', function(d, i) { return i * barHeight; })
    .attr('fill', 'steelblue')
    .on('mouseover', function(d){
      d3.select(this).attr('fill', 'red')
      
      //svg.selectAll('rect').attr('fill', 'red')
      d3.selectAll('rect').attr('fill', 'red')
      
    })
    

Run from R via r2d3::r2d3("sample.js", data=c(0.3, 0.6, 0.8, 0.95, 0.40, 0.20))


Solution

  • By default r2d3 places the visualization in a shadow DOM. Both d3.select and d3.selectAll start their search at the root element of the DOM, but do not venture into a shadow DOM's child nodes.

    Neither select nor selectAll will cross into a shadow tree from the current tree being searched. As svg is a selection of an element within the shadow DOM, the rectangles can be found with svg.selectAll("rect") but not d3.selectAll("rect") (the rects aren't "shadowed" relative to the SVG).

    The easiest solution is to not create a shadow DOM by setting the r2d3 shadow option to false.

    eg (using the base example from r2d3's documentation):

    r2d3(options(r2d3.shadow = FALSE), data=c(0.3, 0.6, 0.8, 0.95, 0.40, 0.20), script = "barchart.js")
    

    Of course in doing so you lose the encapsulation offered by the shadow root, but this might be preferable or neutral depending on the situation.