D3,嵌套追加和数据stream
我正在学习D3的过程中,偶然发现了一个我一直无法find答案的问题。 我不确定,如果我的问题是因为我没有习惯图书馆的惯例,或者是因为我目前没有意识到的程序。 我还应该提到,我只是在六月份才开始做与networking有关的事情,所以我对JavaScript还是比较陌生的。
假设我们正在构build一个工具,可以为用户提供各种图像的食物列表。 并且让我们添加额外的约束条件,即每个列表项需要用一个唯一的ID来标记,以便它可以链接到另一个视图。 解决这个问题的第一个直觉就是创build一个带有自己ID的<div>
列表,每个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; });
现在,如何在其中添加一个带有<p>
的<div>
? 我原来的想法是做一些事情:
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
上追加。
我发现在另一篇文章中提到了嵌套select,该文章指向http://bost.ocks.org/mike/nest/ 。 但是是嵌套的select,因此将附加分解为三个块,这种情况适当/习惯性? 或者,实际上是否有一种构造良好的方法来在一个声明链中形成这种结构? 看起来好像在https://github.com/mbostock/d3/wiki/Selections中提到的子select可能有一种方法,但是我对语言不够熟悉以检验这个假设。
从概念层面来看,这三个对象( div
, p
和img
)更像是一个组,而不是单独的实体,如果代码也反映出来,这将是很好的。
您不能在一个链接命令中添加多个子元素。 您将需要将父项select保存在variables中。 这应该做你想要的:
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/
你想知道像p
或img
这样的子元素如何访问绑定到父元素的数据。 当您添加新元素时,数据将自动从父级inheritance。 这意味着p和img元素与父div具有相同的绑定数据。
这种数据传播对于append
方法来说并不是唯一的。 它发生在以下selection
方法: append
, insert
和select
。
例如,对于selection.append:
selection.append(名称)
将指定名称的新元素添加为当前select中每个元素的最后一个子元素。 返回包含附加元素的新select。 每个新元素都inheritance当前元素的数据(如果有的话),方式与select子select相同。 该名称必须被指定为一个常量,但将来我们可能允许附加现有元素或函数来dynamic生成名称。
如果事情不清楚,请随时询问有关细节。
编辑
您可以通过使用selection.each
方法添加多个子元素,而不将其存储在variables中。 然后您也可以直接从父级访问数据:
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); });
再次,没有实质的不同,但我的首选方法是使用“呼叫”
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; }); });
你不需要存储任何variables,如果你想在其他地方使用类似的结构,你可以分解被调用的函数。
这与nautat的回答并没有根本的区别,但是我认为通过保存更新select而不是inputselect,代码可以变得更简洁一些,并且从需要它的一个操作中获得inputselect(添加周边div)。
在enter()select中插入或追加一个元素时,它会被添加到随后可以使用的更新select中。 这意味着您可以join数据,然后添加一个带有inputselect的div,然后在添加更新选项时,您将添加到您在inputselect中添加的div中:
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 });
#cookie-jar div { border: solid 1px black; }
<script src="ajax/libs/d3/3.4.11/d3.min.js"></script> <div id="cookie-jar"></div>