d3js:添加具有不同数据的相同类型的元素

时间:2013-12-03 23:42:23

标签: javascript jquery d3.js

//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); })

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

我认为我对这里发生的事情有直觉,但有人可以解释究竟是做了什么以及如何解决问题?

可能的参考:

  

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

4 个答案:

答案 0 :(得分:1)

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

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

//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); })

答案 1 :(得分:1)

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

//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); })

如果圈子在不同的组中,则不会被覆盖

答案 2 :(得分:0)

发生的事情是你:

在FIRST HALF中:

  1. 获取svg容器中的所有循环元素。这没有任何回报,因为这是你第一次调用它所以还没有圆形元素。
  2. 然后你加入数据(通过索引,当你没有指定键功能时的默认值)。这会将priceData数据集中的所有内容放入“enter”部分。
  3. 然后你画出你的圈子,一切都很开心。
  4. 然后,在第二部分:

    1. 您再次选择所有圈元素,其中SVG中已存在(priceData.length)元素。
    2. 您正在通过索引加入完全不同的数据集到这些元素,因为您没有指定键功能。这是将(priceData.length)元素放入数据连接的“更新部分”,并且:
      • if priceData.length> difficultyData.length,它将(priceData.length - difficulty.length)元素放入“退出部分”
      • if priceData.length< difficultyData.length,它将(difficulty.length - priceData.length)元素放入“输入部分”
        1. 无论哪种方式,第一个“priceData”一半的所有现有元素都会被重复使用,并使用索引到索引的映射用新的difficultyData覆盖__data__。 / LI>
    3. <强>解决方案吗

      • 我认为关键功能不是你想要的。一个关键的功能是在数据中选择一个唯一字段来连接数据而不是索引,因为索引不关心数据或元素是否重新排序等等。当我想确保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); })
      

      //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); })

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

答案 3 :(得分:0)

我和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');