【问题标题】:d3js: adding same type of elements with different datad3js:添加具有不同数据的相同类型的元素
【发布时间】:2013-12-03 23:42:23
【问题描述】:
//add circles with price data
svgContainer.selectAll("circle")
  .data(priceData)
  .enter()
  .append("svg:circle")
  .attr("r", 6)
  .style("fill", "none")
  .style("stroke", "none")
  .attr("cx", function(d, i) {
    return x(convertDate(dates[i]));
  })
  .attr("cy", function(d) { return y1(d); })

//add circles with difficulty data
svgContainer.selectAll("circle")
  .data(difficultyData)
  .enter()
  .append("svg:circle")
  .attr("r", 6)
  .style("fill", "none")
  .style("stroke", "none")
  .attr("cx", function(d, i) {
    return x(convertDate(dates[i]));
  })
  .attr("cy", function(d) { return y2(d); })

在前半部分,带有价格数据的圆圈沿着图表中的相关线添加。现在我想对后半部分做同样的事情,将具有不同数据的圆圈添加到不同的行。但是,第一个圆圈的数据被第二个圆圈的数据覆盖,第二个圆圈永远不会被绘制。

我想我对这里发生的事情有一种直觉,但是有人可以解释一下到底在做什么以及如何解决问题吗?

可能的参考:

"键功能还决定进入和退出选择: 旧数据中没有对应键的新数据 成为输入选择,以及没有的旧数据 新数据中的对应键成为退出选择。这 剩余数据成为默认更新选择。”

【问题讨论】:

    标签: javascript jquery d3.js


    【解决方案1】:

    首先,了解selectAll()data()enter() 从这个伟大的post 中做了什么。

    问题是由于circle元素在我们进入下半场时已经存在,新提供的数据只是覆盖了圆圈而不是创建新的圆圈。为了防止这种情况发生,需要在后半部分的data()函数中指定一个key function。然后,第一批圈子不会被覆盖。

    //add circles with price data
    svgContainer.selectAll("circle")
      .data(priceData)
      .enter()
      .append("svg:circle")
      .attr("r", 6)
      .style("fill", "none")
      .style("stroke", "none")
      .attr("cx", function(d, i) {
        return x(convertDate(dates[i]));
      })
      .attr("cy", function(d) { return y1(d); })
    
    //add circles with difficulty data
    svgContainer.selectAll("circle")
      .data(difficultyData, function(d) { return d; }) // SPECIFY KEY FUNCTION
      .enter()
      .append("svg:circle")
      .attr("r", 6)
      .style("fill", "none")
      .style("stroke", "none")
      .attr("cx", function(d, i) {
        return x(convertDate(dates[i]));
      })
      .attr("cy", function(d) { return y2(d); })
    

    【讨论】:

    • 如果您采用这种方法,如果您打算使用新数据更新页面,您可能希望在前半部分和后半部分添加关键功能。
    • 我不明白为什么这个答案被选为“最佳”。只需使用组(正如其他人所建议的那样)。当您想到 DOM 模型的层次结构时,使用组也很直观。在这里使用关键函数方法(在我看来)是一种 hack,它使代码在后期更难阅读。
    【解决方案2】:

    您可以将圆圈附加到两个不同的组中,例如:

    //add circles with price data
    svgContainer.append("g")
          .attr("id", "pricecircles")
          .selectAll("circle")
          .data(priceData)
          .enter()
          .append("svg:circle")
          .attr("r", 6)
          .style("fill", "none")
          .style("stroke", "none")
          .attr("cx", function(d, i) {
            return x(convertDate(dates[i]));
          })
          .attr("cy", function(d) { return y1(d); })
    
    //add circles with difficulty data
    svgContainer.append("g")
      .attr("id", "datacircles")
      .selectAll("circle")
      .data(difficultyData)
      .enter()
      .append("svg:circle")
      .attr("r", 6)
      .style("fill", "none")
      .style("stroke", "none")
      .attr("cx", function(d, i) {
        return x(convertDate(dates[i]));
      })
      .attr("cy", function(d) { return y2(d); })
    

    如果圆圈在不同的组中,它们不会被覆盖

    【讨论】:

      【解决方案3】:

      我和 OP 有同样的问题。而且,我想出了一个类似于上面 tomtomtom 的解决方案。简而言之:使用 SVG 组元素 用不同的数据但相同类型的元素来做你想做的事情。关于为什么 SVG 组元素在 D3.js 中如此有用的更多解释,一个很好的例子可以在这里找到:

      https://www.dashingd3js.com/svg-group-element-and-d3js

      我在这里的回复包括一个jsfiddle 的示例,该示例涉及 2 个不同的数据集,它们都在同一个 SVG 上同时可视化,但具有不同的属性。如下所示,我创建了两个不同的组元素(circleGroup1 和 circleGroup2),它们分别处理不同的数据集:

      var ratData1 = [200, 300, 400, 600]; 
      var ratData2 = [32, 57, 112, 293];
      
      var svg1 = d3.select('body')
                  .append('svg')
                    .attr('width', 500)
                    .attr('height', 400);
      
      var circleGroup1 = svg1.append("g");
      var circleGroup2 = svg1.append("g");
      
      circleGroup1.selectAll("circle")
          .data(ratData1)
           .enter().append("circle")
          .attr("cy", 60)
          .attr("cx", function(d, i) { return i * 100 + 30; })
          .attr("r", function(d) { return Math.sqrt(d); });
      
      circleGroup2.selectAll("circle")
          .data(ratData2)
        .enter()
        .append("circle")
        .attr("r", function(d, i){
              return i*20 + 5;
          })
          .attr("cy", 100)
          .attr("cx", function(d,i){ return i*100 +30;})
          .style('fill', 'red')
          .style('fill-opacity', '0.3'); 
      

      【讨论】:

        【解决方案4】:

        发生的事情是你是:

        上半场:

        1. 获取 svg 容器中的所有圆形元素。这不会返回任何内容,因为这是您第一次调用它,所以还没有圆形元素。
        2. 然后您将加入数据(按索引,当您未指定键函数时的默认值)。这会将 priceData 数据集中的所有内容放入“输入”部分。
        3. 然后你画你的圈子,一切都很开心。

        那么,在第二部分:

        1. 您再次选择一般所有圆形元素,其中有 (priceData.length) 元素已存在于 SVG 中。
        2. 您正在将完全不同的数据集连接到这些元素,同样是通过索引,因为您没有指定键函数。这将 (priceData.length) 元素放入数据连接的“更新部分”,并且:
          • 如果 priceData.length >难度数据.length,则将 (priceData.length -难度.length) 元素放入“退出部分”
          • 如果priceData.length
          • 无论如何,前半部分“priceData”中的所有现有元素都将被重用,并使用索引到索引的映射将其__data__ 覆盖为新的难度数据。

        解决方案?

        • 我认为您在这里寻找的不是关键功能。一个关键功能是在数据中选择一个唯一字段来连接数据而不是索引的方法,因为索引不关心数据或元素是否重新排序等。当我想确保 单个数据集在我执行selectAll(..).data(..) 时正确映射回自身。

        • 我会为您的问题使用的解决方案是使用样式类对圆圈进行分组,这样您就可以为不同的数据集创建两组完全独立的圆圈。请参阅下面的更改。

          • 另一种选择是将两组圆嵌套在各自的“svg:g”元素中,并在该元素上设置一个类或ID。然后在您的 selectAll.. 中使用该元素。但通常,您需要以某种方式对它们进行分组,以便您可以通过这些分组来选择它们。

        //add circles with price  data
        svgContainer.selectAll("circle.price")
            .data(priceData)
            .enter()
            .append("svg:circle")
            .attr("class", "price")
            .attr("r", 6)
            .style("fill", "none")
            .style("stroke", "none")
            .attr("cx", function(d, i) {
                return x(convertDate(dates[i]));
            })
            .attr("cy", function(d) { return y2(d); })
        
        //add circles with difficulty data
        svgContainer.selectAll("circle.difficulty")
            .data(difficultyData)
            .enter()
            .append("svg:circle")
            .attr("class", "difficulty")
            .attr("r", 6)
            .style("fill", "none")
            .style("stroke", "none")
            .attr("cx", function(d, i) {
                return x(convertDate(dates[i]));
            })
            .attr("cy", function(d) { return y2(d); })
        

        使用此方法,您将始终为单独的数据集使用正确的圆形元素。之后,如果你在数据中拥有比单纯使用索引更好的唯一值,还可以在两个.data(..)调用中添加自定义键函数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-03-19
          • 1970-01-01
          • 1970-01-01
          • 2018-01-15
          • 2017-03-24
          • 1970-01-01
          • 2010-11-21
          相关资源
          最近更新 更多