title: Errors Using `d3.legend`: Uncaught TypeError: node.getAttribute is not a function
body: <p>I am trying to add a D3 legend to my donut chart, and I followed the example here: <a href="http://bl.ocks.org/ZJONSSON/3918369" rel="nofollow noreferrer">http://bl.ocks.org/ZJONSSON/3918369</a>. The donut chart renders fine. </p>
  
  <p>However, I am running into below errors:</p>
  
  <p>Inside the d3.legend() function, I am getting this error 'Uncaught TypeError: node.getAttribute is not a function' and 'Uncaught TypeError: this.querySelectorAll is not a function'. </p>
  
  <p>I am not sure why...any thoughts?</p>
  
  <pre><code>// Alerts.js
  renderBody() {
      const {list, currentProgram} = this.props
      const width = 260;
      const height = 260;
      const radius = width/2; // 130
  
      // arc &amp; label generator
      let arc = d3.arc()
        .outerRadius(radius - 10)
        .innerRadius(90)
        .padAngle(0.02);
  
      let labelArc = d3.arc()
        .outerRadius(radius + 60)
        .innerRadius(radius - 90);
  
      // pie generator
      let pie = d3.pie()
        .sort(null)
        .value( d =&gt; { return d; });
  
      // define svg
      let svg = d3.select('.enrolled-statistics-svg').append('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g') // group similar elements together
        .attr('transform', 'translate(' + width/2 + ', ' + height/2 + ')');
  
      // append g elements (arc)
      const g = svg.selectAll('.arc')
        .data(pie(testData))
        .enter().append('g')
        .attr('class', 'arc');
  
      // append the path of the arc
      g.append('path')
        .attr('d', arc)
        .attr('data-legend', val =&gt; {
          return val.data;
        })
        .attr('data-legend-pos', (val) =&gt; {
          return val.index;
        })
        .style('fill', val =&gt; {
          return COLOR_ARRAY[val.index];
        });
  
      // append with label
      g.append('text')
        .attr('transform', d =&gt; {
          return 'translate(' + labelArc.centroid(d) + ')';
        })
        .attr('dy', '0.35em')
        .text( val =&gt; {
          return (val.data / testResp.data.enrolledStatistics.total) * 100 + '%';
        });
  
      // define d3.legend()
      d3.legend = (g) =&gt; {
  
        g.each(() =&gt; {
          let g = d3.select(this);
          console.log('------- g: ', g);
  
          let items = {};
          let svg = d3.select(g.property('nearestViewportElement'));
          let legendPadding = g.attr('data-style-padding') || 5; // ERROR: Uncaught TypeError: node.getAttribute is not a function
          let legendBox = g.selectAll('.legend-box').data([true]); // ERROR: Uncaught TypeError: this.querySelectorAll is not a function
          let legendItems = g.selectAll('.legend-items').data([true]);
  
          legendBox.enter().append('rect').classed('legend-box', true);
          legendItems.enter().append('g').classed('legend-items', true);
  
          svg.selectAll('[data-legend]').each(() =&gt; {
            let self = d3.select(this)
            items[self.attr('data-legend')] = {
              pos: self.attr("data-legend-pos") || this.getBBox().y,
              color: self.attr("data-legend-color") != undefined ? self.attr("data-legend-color")
                : self.style("fill") != 'none' ? self.style("fill") : self.style("stroke")
            }
          });
  
          items = d3.entries(items).sort(function (a, b) {
            return a.value.pos - b.value.pos
          });
  
          console.log('------- items: ', items);
  
          legendBox.selectAll('text')
            .data(items, val =&gt; val.key)
            .call(val =&gt; {
              val.enter().append('text')
            })
            .call(val =&gt; {
              val.exit().remove()
            })
            .attr('y', (val, i) =&gt; {
              return `${i}em`
            })
            .attr('x', '1em')
            .text(val =&gt; val.key);
  
          legendItems.selectAll("circle")
            .data(items, function (d) {
              return d.key
            })
            .call(val =&gt; val.enter().append("circle"))
            .call(val =&gt; val.exit().remove())
            .attr('cy', (val, i) =&gt; `${i - 0.25}em`)
            .attr('cx', 0)
            .attr('r', '0.4em')
            .style('fill', val =&gt; {
              console.log(val.value.color);
              return val.value.color
            });
  
          // Reposition and resize the box
          let lbbox = li[0][0].getBBox();
  
          lbbox.attr("x", (lbbox.x - legendPadding))
            .attr("y", (lbbox.y - legendPadding))
            .attr("height", (lbbox.height + 2 * legendPadding))
            .attr("width", (lbbox.width + 2 * legendPadding));
        });
  
        return g;
      }
  
      // define legend svg
      let padding = 20;
      let legx = radius + padding;
      let legendSVG = d3.select('.enrolled-statistics').append('svg')
        .attr('width', 150)
        .attr('height', 150)
        .append('g')
        .attr('class', 'legend')
        .attr('transform', 'translate(' + legx + ', 0)')
        .style('font-size', '12px')
        .style('fill', 'blue')
        .call(d3.legend);
  
       return(....)
      }
  </code></pre>