使用`d3.legend`时出错:未捕获的TypeError:node.getAttribute不是函数 [英] Errors Using `d3.legend`: Uncaught TypeError: node.getAttribute is not a function

查看:104
本文介绍了使用`d3.legend`时出错:未捕获的TypeError:node.getAttribute不是函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将D3图例添加到我的甜甜圈图中,并且我在此处遵循以下示例: http ://bl.ocks.org/ZJONSSON/3918369 .甜甜圈图呈现良好.

但是,我遇到以下错误:

在d3.legend()函数内部,出现此错误未捕获的TypeError:node.getAttribute不是函数"和未捕获的TypeError:this.querySelectorAll不是函数".

我不确定为什么...有什么想法吗?

// 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(....)
    }

解决方案

您正在使用箭头函数定义each回调.这会更改this关键字的范围更多信息这里).因此,基本上d3.select(this)不会选择您期望的内容.另外,您在两种不同的方法中将g用作变量,这可能会导致意外的行为(我认为;我建议您使用具有不同名称的变量来避免混淆).

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(....)
    }

解决方案

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

这篇关于使用`d3.legend`时出错:未捕获的TypeError:node.getAttribute不是函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆