我有一个像

[
    {"name":"foo", "links":["a", "b", "c"]},
    {"name":"bar", "links":["d", "e", "f"]}
]

我想列一个 list
<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 的特定部分进行操作。

任何帮助,将不胜感激!

最佳答案

调用 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 )上存储一个 bool 值来完成此操作,也可以通过查询 DOM 以查看子菜单是否已存在来完成: d3.select(this).select("ul").empty()

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

现场示例:http://bl.ocks.org/1541816

关于d3.js : create an expanding list viz using selection. 上,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8683555/

10-12 21:48