d3.js:使用selection.on创建一个扩展列表

时间:2011-12-30 20:06:49

标签: d3.js

我有一个像

这样的数据结构
[
    {"name":"foo", "links":["a", "b", "c"]}, 
    {"name":"bar", "links":["d", "e", "f"]}
]

我想列出像

这样的列表
<ul>
    <li> foo </li>
    <li> bar </li>
</ul>

当我点击foo时,展开如

<ul>
    <li> foo 
        <ul>
            <li> a </li>
            <li> b </li>
            <li> c </li>
        </ul>
    </li>
    <li> bar </li>
</ul>

我想用d3.js这样做。制作顶级名单很简单有趣:

toplist = d3.select("body").append("ul");
toplist.selectAll("li")
    .data(data)
    .enter()
    .append("li")
        .text(function(d){return d.name;})

添加子列表甚至更容易:

items = toplist.append("ul")
    .selectAll("li")
    .data(function(d){return d.links})
    .enter()
    .append("li")
         .text(function(d){return d})

我可以为on("click"..中的每个li添加toplist,例如

toplist.selectAll("li")
    .data(data)
    .enter()
    .append("li")
        .text(function(d){return d.name;})
        .on("click", expand)

并将子列表代码移动到名为expand的函数中。它看起来像

function expand(d,i){

然后在其中包含上述items = ...代码。

问题是,这会同时扩展foobar

如何将foo列表元素单独发送到bar的单击事件,该元素仅扩展该元素?本质上,我需要传递expand数据元素的索引,并让它只对DOM的特定部分进行操作。

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:9)

调用expand时,您拥有所需的一切。 this上下文是单击的li元素,d是关联数据。如果您只想展开(而不是在展开和折叠之间切换),那么您可以通过选择以下内容来创建子列表:

function expand(d) {
  d3.select(this)
      .on("click", null)
    .append("ul")
    .selectAll("li")
      .data(d.links)
    .enter().append("li")
      .text(function(d) { return d; });
}

请注意,expand还会从点击的项目中删除点击处理程序;单击两次时,您不想再次添加列表。

如果要在展开和删除之间切换,则需要跟踪某些状态:子菜单是打开还是关闭。您可以通过在dd.open,例如)上存储布尔值来执行此操作,或者您可以通过查询DOM以查看子菜单是否已存在来执行此操作:d3.select(this).select("ul").empty()

另一种可以做到这一点的方法是提前创建所有子菜单,然后通过display样式属性切换可见性。如果您拥有所有可用数据,这是最简单的选择;如果您异步加载链接数据,我可能只会动态创建子菜单。

实例:http://bl.ocks.org/1541816