D3,嵌套附加和数据流

时间:2012-11-02 22:29:24

标签: javascript d3.js

我正在最终学习D3,我偶然发现了一个我无法找到答案的问题。我不确定我的问题是因为我没有习惯性地使用库,或者是因为我目前没有意识到的程序。我还应该提一下,我在6月才开始做与网络相关的事情,所以我对javascript很新。

假设我们正在构建一个工具,为用户提供包含相应图像的食物列表。并且让我们添加额外的约束,每个列表项需要用唯一的ID标记,以便它可以链接到另一个视图。我解决此问题的第一个直觉是创建一个<div>列表,每个列表都有自己的ID,其中每个div都有自己的<p><img>。生成的HTML看起来像:

<div id="chocolate">
  <p>Chocolate Cookie</p>
  <img src="chocolate.jpg" />
</div>
<div id="sugar">
  <p>Sugar Cookie</p>
  <img src="sugar.jpg" />
</div>

此工具的数据位于JSON数组中,其中单个JSON如下所示:

{ "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }

有没有办法一举生成HTML?从添加div的基本情况开始,代码可能类似于:

d3.select(containerId).selectAll('div')                                                          
   .data(food)
   .enter().append('div')
   .attr('id', function(d) { return d.label; });

现在,添加<div>并添加<p>怎么样?我原来的想法是做一些像:

d3.select(containerId).selectAll('div')                                                          
   .data(food)
   .enter().append('div')
   .attr('id', function(d) { return d.label; })
       .append('p').text('somethingHere');

但是,我发现有两个问题:(1)如何从div元素获取数据,以及(2)如何在一个声明性链中将多个子节点附加到同一个父节点?我想不出一种方法可以在我img附加第三步。

我发现在另一篇帖子中提到了嵌套选择,该帖子指向http://bost.ocks.org/mike/nest/。但是嵌套选择,因此将附加分成三个块,适合/惯用于这种情况?或者实际上是否有一种构造良好的方式在一个声明链中形成这个结构?好像https://github.com/mbostock/d3/wiki/Selections上提到的子选择可能有一种方法,但我对该语言的测试不够熟悉。

从概念层面来看,这三个对象(divpimg)更像是一个组而不是单独的实体,如果代码会更好反映了这一点。

3 个答案:

答案 0 :(得分:74)

您无法在一个链式命令中添加多个子元素。您需要将父选择保存在变量中。这应该做你想要的:

var data = [{ "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" },
        { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }];

var diventer = d3.select("body").selectAll("div")
    .data(data)
  .enter().append("div")
    .attr("id", function(d) { return d.label; });

diventer.append("p")
    .text(function(d) { return d.text; });

diventer.append("img")
    .attr("src", function(d) { return d.img; });​

见工作小提琴:http://jsfiddle.net/UNjuP/

您想知道像pimg这样的子元素如何访问绑定到其父元素的数据。在追加新元素时,数据会自动从父项继承。这意味着p和img元素将具有与父div相同的数据绑定。

此数据传播不是append方法的唯一选择。它采用以下selection方法:appendinsertselect

例如,对于selection.append:

  

selection.append(名称)

     

追加具有指定名称的新元素作为其中的最后一个子元素   当前选择中的每个元素。返回一个新选择   包含附加的元素。每个新元素都继承数据   如果有的话,当前元素的选择方式与选择的方式相同   subselections。必须将名称指定为常量,但在   未来我们可能允许将现有元素或函数附加到   动态生成名称。

如果不清楚,请随时询问详情。


修改

您可以使用selection.each方法添加多个子元素,而无需将选区存储在变量中。然后,您还可以直接访问父级数据:

var data = [{ "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" },
        { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }];

d3.select("body").selectAll("div")
    .data(data)
  .enter().append("div")
    .attr("id", function(d) { return d.label; })
    .each(function(d) {
        d3.select(this).append("p")
          .text(d.text);
        d3.select(this).append("img")
          .attr("src", d.img);
    });

答案 1 :(得分:22)

同样,没有实质性的不同,但我首选的方法是使用'call'

var data = [{ "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" },
        { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }];

d3.select("body").selectAll("div")
    .data(data)
  .enter().append("div")
    .attr("id", function(d) { return d.label; })
  .call(function(parent){
    parent.append('p').text(function(d){ return d.text; });
    parent.append('img').attr("src", function(d) { return d.img; });​
  });

如果你想在其他地方使用类似的结构,你不需要存储任何变量,你可以将被调用的函数分解出来。

答案 2 :(得分:3)

这与nautat's answer根本不同,但我认为通过保存更新选择而不是输入<可以使代码更清晰一些/ em>选择,并从中获取输入选项以进行需要它的一个操作(添加周围的div)。

当您在 enter()选项中插入或附加元素时,它会添加到您之后可以使用的更新选择中。这意味着您可以加入数据,然后使用输入选择添加div,然后当您使用更新选择附加时,您将在中添加在您添加的div中输入选择:

&#13;
&#13;
var cookies = [
  { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" },
  { "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" }];

var cookie = d3.select("#cookie-jar").selectAll().data(cookies);
cookie.enter().append("div");
cookie.append("p").text(function(d){ return d.text });
cookie.append("img").attr("src",function(d){ return d.img });
&#13;
#cookie-jar div { border: solid 1px black; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="cookie-jar"></div>
&#13;
&#13;
&#13;