如何为每个数据更新多个元素

3

有很多关于如何更新每个数据的dom的文章。但是如果我想为每个数据更新多个dom节点呢?

例如,对于每个数据,根据数据中的数字生成div和span。

[
  [1, 2],
  [2, 1],
]

我想像yield一样

<div class="data">
  <div>1</div><span>2</span><span>2</span>
</div>
<div class="data">
  <div>2</div><div>2</div><span>1</span>
</div>

应该以以下方式开始

const selection = d3.selectAll("div.data");
selection.exit().remove();
selection.enter().append("div").attr("class", "data").merge(selection); 
// Then I don't know how to add multiple dom according to the data value.

对于从 d3.selectAll("div.data") 获取的每个 <div class="data"> 元素,您可以使用 .children 获取其相应的子元素。然后,您可以循环遍历这些子元素,这只是一个嵌套循环。 - Lonnie Best
4
感谢Lonnie的关注。你的方法可能可行,但最好使用d3进行操作。我们使用d3是为了避免直接操作DOM。 - Jiajun
2个回答

2
首先,您的问题标题(“如何为每个数据更新多个元素”)表明您想要更新这些元素,但实际上,通过阅读您的实际问题,您只是想创建它们(在D3术语中是“输入”)。此外,请注意,您不能在d3.selectAll()之后使用data()
话虽如此,有几种解决方案。其中之一是对外部div使用each(),然后使用d3.range()添加您想要的元素数量(div或span),它将创建一个数组,其长度基于您传递给它的数字。该数组的实际内容无关紧要,因为div和span中的文本与内部数组中的数字相对应。
以下是一个简单的示例,请注意每个元素(外部div、内部div和span)的颜色:

const body = d3.select("body");
const data = [
  [1, 2],
  [2, 1],
];
const dataDiv = body.selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .attr("class", "data")
  .each(function(d) {
    d3.select(this).selectAll(null)
      .data(d3.range(d[0]))
      .enter()
      .append("div")
      .attr("class", "innerDiv")
      .html(d[0]);
    d3.select(this).selectAll(null)
      .data(d3.range(d[1]))
      .enter()
      .append("span")
      .html(d[1]);
  })
.data {
  margin: 10px;
  padding: 2px;
  display: inline-block;
  background-color: wheat;
}

.innerDiv {
  margin: 10px;
  padding: 2px;
  display: inline-block;
  background-color: yellowgreen;
}

span {
  margin: 10px;
  padding: 2px;
  background-color: salmon;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

以下是生成的HTML代码:

<div class="data">
  <div class="innerDiv">1</div><span>2</span><span>2</span>
</div>
<div class="data">
  <div class="innerDiv">2</div>
  <div class="innerDiv">2</div><span>1</span>
</div> 

谢谢你的回答! 首先,我确实是指更新。我在enter().append()之后有一个merge()。此外,我还有一个exit().remove()。所以如果它不存在,就创建它。如果存在,则更新它。 是的,你的代码运行得很好,符合预期。 由于你正在使用5.7版本,我可以将enter替换为join并适应我的目的。 - Jiajun

-2

d3支持选择器的嵌套。关键在于将数据绑定到父元素后,调用parent.selectAll(),然后使用函数而不是变量来绑定数据,例如parent.selectAll(child).data(d => d)。d3将自动将嵌套数组绑定到每个子元素。这在链接中有描述,其中定义了一个具有嵌套trtd元素的table

数据操作符为每个组定义数据数组。在这里,数据函数对每行分别调用一次,并连续传递每个父数据。由于父数据是一个数组的数组,因此数据函数只需为每个单元格行返回内部数组。

 let data = [
   [1, 2],
   [2, 1],
 ]

 let selection =  d3.selectAll('div.data')
 .data(data)
 .enter()
 .append("div")
 .attr("class", "data")

 selection
 .selectAll('span')
 .data(function(d) { 
  return  d;
 })
.enter()
.append('span')
.text(d => d)
.data span {
 background: white;
 border: 1px solid black;
 padding: 5px;
 margin: 5px;
}

.data {
 background: yellow;
 margin: 10px;
 border: 1px solid red;
 padding: 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接