我有一个像
这样的数据结构[
{"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 = ...
代码。
问题是,这会同时扩展foo
和bar
。
如何将foo
列表元素单独发送到bar
的单击事件,该元素仅扩展该元素?本质上,我需要传递expand
数据元素的索引,并让它只对DOM的特定部分进行操作。
任何帮助将不胜感激!
答案 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
还会从点击的项目中删除点击处理程序;单击两次时,您不想再次添加列表。
如果要在展开和删除之间切换,则需要跟踪某些状态:子菜单是打开还是关闭。您可以通过在d
(d.open
,例如)上存储布尔值来执行此操作,或者您可以通过查询DOM以查看子菜单是否已存在来执行此操作:d3.select(this).select("ul").empty()
。
另一种可以做到这一点的方法是提前创建所有子菜单,然后通过display
样式属性切换可见性。如果您拥有所有可用数据,这是最简单的选择;如果您异步加载链接数据,我可能只会动态创建子菜单。