【问题标题】:Is there a way to make a D3 legend change based on user input to radio buttons?有没有办法根据用户对单选按钮的输入来更改 D3 图例?
【发布时间】:2021-01-09 03:17:55
【问题描述】:

我的目标是创建一个展示数据驱动图例和单选按钮的网页。我希望用户选择单选按钮来更改图例。

复杂的因素是图例依赖于嵌套函数,我不知道如何使嵌套函数的“键”响应单选按钮。

以下是我目前采取的步骤:

  • 创建图例(嵌套数据,然后将嵌套键附加到矩形)。
  • 单击单选按钮,使 d3 选择 DOM 中的所有矩形。
  • 将单选按钮的选择发送到名为updateKey 的函数,其中包含嵌套变量。用单选按钮选择替换嵌套的d.key 部分。
  • 将矩形的填充属性改为color(d.key)

我的问题是,嵌套变量在 updateKey 函数中似乎没有变化,尽管我对全局变量的研究表明它 should 发生了变化。

代码在这里:

var doc = URL.createObjectURL(new Blob([`Passed Category    Owner   Dup
    yes msg Hailey  yes
    yes msg Hailey  yes
    yes doc Hailey  yes
    yes doc Robert  yes
    yes msg Laquila yes
    no  spreadsheet Hailey  yes
    `]));

d3.tsv(doc)
  .row(function(d) {
    return {
      Passed: d.Passed,
      Category: d.Category,
      Owner: d.Owner,
      Dup: d.Dup
    };
  })
  .get(function(error, data) {

    var svg = d3.select("#chart").append("svg")
      .append("g");
    //create a function for applying colours
    var cat20 = d3.schemeCategory20

    var colors = d3.scaleOrdinal()
      .domain(function(d) {
        return nest(d.key);
      })
      .range(cat20);
    //pick one column of data and nest according to that column
    nest = d3.nest()
      .key(function(d) {
        return d.Category
      })
      .sortKeys(d3.descending)
      .entries(data)

    var legend = d3.select("#legend")
      //Make the legend
      .append("svg")
      .attr("class", "legend")
      .selectAll("g")
      .data(d3.range(20))
      .enter()
      .append("g")
      .attr("transform", function(d, i) {
        return "translate(0," + i * 25 + ")";
      });
    //make the legend squares
    legend.append("rect")
      .data(nest)
      .attr("class", "rect")
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", function(d) {
        return colors(d.key)
      })
      .style('stroke', "Grey");
    //make the legend text
    legend.append("text")
      .data(nest)
      .attr("x", 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .text(function(d) {
        return d.key;
      });

    function updateKey(h) {

      var nest = d3.nest()
        .key(function(d) {
          return (h)
        })
        .sortKeys(d3.descending)
        .entries(data)
    }
   
    d3.selectAll(("input[name='stack']")).on("change", function(d) {
      var colVar = this.value;
      if (colVar == 'Category') {
        d3.selectAll(".rect").style("fill", function(d) {
          updateKey("d['" + colVar + "']")
          return colors(d.key)
        })
      }
    })


  })

更新:我已经确认该函数正在检索一个新的嵌套变量,因为当我在最后一段代码中输入console.log(nest) 时,它返回了正确的键值对象。但是,如果我在console.log(nest) 的正下方添加console log(d.key),它会返回在代码开头设置的嵌套变量的键。这表明d.key 在某种程度上没有改变,即使嵌套在改变。我想我需要明确设置 d.key。

更新 2:我的新策略是创建所有图例,然后使用按钮根据类别显示/消失图例。

【问题讨论】:

  • 您可以添加一个活动类并使用 css 更改颜色吗? .active.legend .rect{fill:pink}
  • 不确定。我还需要更改文本,我认为 css 不会这样做。
  • 创建工作代码片段。

标签: javascript html d3.js


【解决方案1】:

我认为您的问题是对 nest 函数的混淆,并且您的 nest 变量不是对该函数的引用,而是对您的数据的引用。

也就是说,我发现您的代码过于混乱,并且没有写到您应该如何使用d3 数据绑定功能。所以这是我评论的重构工作示例

<!DOCTYPE html>

<html>
  <head>
    <script src="https://d3js.org/d3.v4.min.js"></script>
  </head>

  <body>
    <input type="radio" id="passed" name="stack" value="Passed" />
    <label for="passed">Passed</label><br />
    <input type="radio" id="category" name="stack" value="Category" checked />
    <label for="category">Category</label><br />
    <input type="radio" id="owner" name="stack" value="Owner" />
    <label for="owner">Owner</label>

    <div id="chart"></div>
    <br/><br/>
    <div id="legend"></div>
    <script>
      var doc = URL.createObjectURL(
        new Blob([
          `Passed,Category,Owner,Dup
yes,msg,Hailey,yes
yes,msg,Hailey,yes
yes,doc,Hailey,yes
yes,doc,Robert,yes
yes,msg,Laquila,yes
no,spreadsheet,Hailey,yes`,
        ])
      );

      // had to switch to csv for stacksnippet editor
      d3.csv(doc)
        .row(function (d) {
          return {
            Passed: d.Passed,
            Category: d.Category,
            Owner: d.Owner,
            Dup: d.Dup,
          };
        })
        .get(function (error, data) {

          // container for legend
          var legend = d3
            .select('#legend')
            //Make the legend
            .append('svg')
            .attr('class', 'legend');

          // initial draw
          drawLegend('Category');

          // function to redraw legend
          // based on d3 enter, update, exit
          function drawLegend(currentSelection) {

            // apply nest function for new selection
            let nestedData = d3
              .nest()
              .sortKeys(d3.descending)
              .key(function (d) {
                return d[currentSelection];
              }).entries(data);

            // update selection
            let lu = legend
              .selectAll('g')
              // the key function here is important
              // so that d3 can calculate the enter, update, exit
              .data(nestedData, d => d.key);

            // exit selection
            lu.exit().remove();

            // enter selection
            // add container g
            let le = lu
              .enter()
              .append('g')
              .attr('transform', function (d, i) {
                return 'translate(0,' + i * 25 + ')';
              });

            // on enter add rect
            le.append('rect')
              .attr('class', 'rect')
              .attr('width', 18)
              .attr('height', 18)
              .style('fill', function (d,i) {
                return d3.schemeCategory20[i];
              })
              .style('stroke', 'Grey');

            // on enter add text
            le.append('text')
              .attr('x', 24)
              .attr('y', 9)
              .attr('dy', '.35em');

            // enter + update
            lu = le.merge(lu);

            // on enter or update, change text
            lu.selectAll('text')
              .text(function(d){
                return d.key;
              });
          }

          d3.selectAll("input[name='stack']").on('change', function (d) {
            drawLegend(this.value);
          });
        });
    </script>
  </body>
</html>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-16
    • 1970-01-01
    • 1970-01-01
    • 2012-09-12
    相关资源
    最近更新 更多