Errors Using `d3.legend`: Uncaught TypeError: node.getAttribute is not a function

无人久伴 提交于 2020-07-10 08:16:44

问题


I am trying to add a D3 legend to my donut chart, and I followed the example here: http://bl.ocks.org/ZJONSSON/3918369. The donut chart renders fine.

However, I am running into below errors:

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'.

I am not sure why...any thoughts?

// Alerts.js
renderBody() {
    const {list, currentProgram} = this.props
    const width = 260;
    const height = 260;
    const radius = width/2; // 130

    // arc & 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 => { 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 => {
        return val.data;
      })
      .attr('data-legend-pos', (val) => {
        return val.index;
      })
      .style('fill', val => {
        return COLOR_ARRAY[val.index];
      });

    // append with label
    g.append('text')
      .attr('transform', d => {
        return 'translate(' + labelArc.centroid(d) + ')';
      })
      .attr('dy', '0.35em')
      .text( val => {
        return (val.data / testResp.data.enrolledStatistics.total) * 100 + '%';
      });

    // define d3.legend()
    d3.legend = (g) => {

      g.each(() => {
        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(() => {
          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 => val.key)
          .call(val => {
            val.enter().append('text')
          })
          .call(val => {
            val.exit().remove()
          })
          .attr('y', (val, i) => {
            return `${i}em`
          })
          .attr('x', '1em')
          .text(val => val.key);

        legendItems.selectAll("circle")
          .data(items, function (d) {
            return d.key
          })
          .call(val => val.enter().append("circle"))
          .call(val => val.exit().remove())
          .attr('cy', (val, i) => `${i - 0.25}em`)
          .attr('cx', 0)
          .attr('r', '0.4em')
          .style('fill', val => {
            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(....)
    }

回答1:


You are using an arrow function to define the each callback. This changes the scope of the this keyword more information here). So basically d3.select(this) does not select what you would expect. Also, you are using g as a variable in two different methods, which could lead to unexpected behaviour (I think; I would advise making variables with distinct names to avoid confusion anyway).



来源:https://stackoverflow.com/questions/48015265/errors-using-d3-legend-uncaught-typeerror-node-getattribute-is-not-a-functio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!