D3、嵌套追加和数据流 [英] D3, nested appends, and data flow

查看:19
本文介绍了D3、嵌套追加和数据流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在最终学习 D3 的过程中,偶然发现了一个我一直无法找到答案的问题.我不确定我的问题是因为我没有用图书馆惯用的方式思考,还是因为我目前不知道的程序.我还要提一下,我是在 6 月份才开始做与网络相关的事情,所以我对 javascript 还是很陌生.

假设我们正在构建一个工具,为用户提供带有相应图像的食物列表.让我们添加附加约束,即每个列表项都需要用唯一的 ID 标记,以便它可以链接到另一个视图.我解决这个问题的第一个直觉是创建一个

的列表,每个列表都有自己的 ID,其中每个 div 都有自己的

;.生成的 HTML 将类似于:

<p>巧克力曲奇</p><img src="chocolate.jpg"/>

<div id="糖"><p>糖饼干</p><img src="sugar.jpg"/>

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

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

有没有办法一举生成HTML?从添加 div 的基本情况开始,代码可能如下所示:

d3.select(containerId).selectAll('div').数据(食物).enter().append('div').attr('id', function(d) { return d.label; });

现在,添加一个带有

怎么样?我最初的想法是做这样的事情:

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

但是,我看到了两个问题:(1) 如何从 div 元素获取数据,以及 (2) 如何在一个声明中将多个子项附加到同一个父项链?我想不出一种方法来进行第三步,我将在 img 上附加.

我在另一篇文章中发现提到嵌套选择,它指向 http://bost.ocks.组织/麦克/巢/.但是嵌套选择,因此将附加内容分成三个块,是否适合这种情况/惯用语?或者实际上是否有一种构造良好的方法可以在一个声明链中形成这种结构?似乎有一种方法可以在 https://github.com/mbostock/d3 上提到子选择/wiki/Selections,但我对语言不够熟悉,无法验证该假设.

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

解决方案

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

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(数据).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 方法中:append插入select.

例如,对于 selection.append:

<块引用>

selection.append(name)

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

如有不清楚的地方,请随时询问详情.

<小时>

编辑

您可以使用 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(数据).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);});

I'm in the process of finally learning D3, and I stumbled upon a problem that I haven't been able to find an answer to. I'm not certain if my question is because I'm not thinking idiomatically with the library, or if it is because of a procedure that I am currently unaware of. I should also mention that I've only started doing web-related things in June, so I'm fairly new to javascript.

Say that we're building a tool that gives a user a list of foods with respective images. And lets add on the additional constraint that each list item needs to be labeled by a unique ID so that it can be linked to another view. My first intuition to solve this is to create a list of <div>'s each with their own ID, where each div has its own <p> and <img>. The resulting HTML would look something like:

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

The data for this tool is in a JSON array, where an individual JSON looks like:

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

Is there a way to do generate the HTML in one fell swoop? Starting with a base case of adding a div, the code might look something like:

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

Now, what about adding a <div> with a <p> in it? My original thought was to do something like:

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

But, I see two problems with this: (1) how do you get the data from the div element, and (2) how can you append multiple children to the same parent in one declarative chain? I can't think of a way to make the third step where I would append on the img.

I found mention of nested selection on another post, which pointed to http://bost.ocks.org/mike/nest/. But is nested selection, and therefore breaking apart the appends into three chunks, appropriate/idiomatic for this situation? Or is there actually a well-constructed way to form this structure in one chain of declarations? It seems like there might be a way with subselections mentioned on https://github.com/mbostock/d3/wiki/Selections, but I'm not familiar enough with the language to test that hypothesis.

From a conceptual level, these three objects (div, p, and img) are treated more like one group rather than separate entities, and it would be nice if the code reflected that as well.

解决方案

You cannot add multiple child elements within one chained command. You will need to save the parent selection in a variable. This should do what you want:

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

See working fiddle: http://jsfiddle.net/UNjuP/

You were wondering how a child element like p or img, gets access to the data that is bound to its parent. The data is inherited automatically from the parent when you append a new element. This means that the p and img elements will have the same data bound to them as the parent div.

This data propagation is not unique for the append method. It happens with the following selection methods: append, insert, and select.

For example, for selection.append:

selection.append(name)

Appends a new element with the specified name as the last child of each element in the current selection. Returns a new selection containing the appended elements. Each new element inherits the data of the current elements, if any, in the same manner as select for subselections. The name must be specified as a constant, though in the future we might allow appending of existing elements or a function to generate the name dynamically.

Feel free to ask about the details if something is not clear.


EDIT

You can add multiple child elements without storing the selection in a variable by using the selection.each method. You can then also directly access the data from the parent:

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

这篇关于D3、嵌套追加和数据流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆