D3如何改变检测工作?

sup*_*ary 1 d3.js

如果我有一个JSON对象数组.D3如何确定哪一组进入enter()

如果我有一个对象数组,如下所示:

var data = [
  {label:'a', value:1},
  {label:'b', value:3},
  {label:'c', value:2}
]
Run Code Online (Sandbox Code Playgroud)

然后我将它绑定到选择:

var bars = vis.selectAll("rect.bar")
  .data(data)
Run Code Online (Sandbox Code Playgroud)

enter集现在包含所有这些数据.如果我现在更改数据:

var data = [
  {label:'a', value:99},
  {label:'c', value:2}
  {label:'b', value:3},
]
Run Code Online (Sandbox Code Playgroud)

D3如何判断哪些值是更新,移动还是新的?它是在对象身份上完成的吗?有没有办法让它使用标签字段来填充输入集?

Ger*_*ado 6

如果我正确理解您的问题,您想知道D3如何将给定对象与给定DOM元素相关联.

默认情况下,如果您未设置键功能(更多内容如下),则对象将按其顺序关联.根据API:

...数据中的第一个数据分配给第一个选定元素,第二个数据分配给第二个选定元素,依此类推.

让我们在下面的例子中看到这一点.第一个数组是这样的:

var data = [{
    label: 'a',
    value: 1
}, {
    label: 'b',
    value: 3
}, {
    label: 'c',
    value: 2
}];
Run Code Online (Sandbox Code Playgroud)

第二个数组也有3个对象.但要注意变化:第一个是标签c,第三个是标签a.他们被交换:

var data2 = [{
    label: 'c',
    value: 99
}, {
    label: 'b',
    value: 2
}, {
    label: 'a',
    value: 3
}];
Run Code Online (Sandbox Code Playgroud)

因此,相对于a第一个数据的条形将获得c第二个数据中的值.查看演示,单击按钮:

var svg = d3.select("svg");

var data = [{
  label: 'a',
  value: 1
}, {
  label: 'b',
  value: 3
}, {
  label: 'c',
  value: 2
}];

var data2 = [{
  label: 'c',
  value: 99
}, {
  label: 'b',
  value: 2
}, {
  label: 'a',
  value: 3
}];

var xScale = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.value)])
  .range([0, 260]);

var yScale = d3.scaleBand()
  .domain(data.map(d => d.label))
  .range([10, 140])
  .padding(0.3);

var rects = svg.selectAll("foo")
  .data(data)
  .enter()
  .append("rect");

rects.attr("x", 40)
  .attr("y", d => yScale(d.label))
  .attr("height", yScale.bandwidth)
  .attr("width", d => xScale(d.value))
  .attr("fill", "teal");
  
d3.axisLeft(yScale)(svg.append("g").attr("transform", "translate(40,0)"))

d3.select("button").on("click", function() {

  xScale.domain([0, d3.max(data2, d => d.value)]);

  rects.data(data2);

  rects.transition()
    .duration(1000)
    .attr("width", d => xScale(d.value))

})
Run Code Online (Sandbox Code Playgroud)
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click</button>
<br>
<svg></svg>
Run Code Online (Sandbox Code Playgroud)

如您所见,单击按钮后的图表显然不正确.

为了避免这种情况,确保先前绑定到标签的DOM元素在数据数组更改时a仍将绑定到该标签a,我们必须使用键函数:

可以指定关键函数来控制将哪个数据分配给哪个元素,替换默认的索引连接.为每个选定的元素评估此关键函数,按顺序传递当前数据(d),当前索引(i)和当前组(节点),并将其作为当前DOM元素(nodes [i]) .然后还为数据中的每个新数据评估关键函数,传递当前数据(d),当前索引(i)和组的新数据,并将其作为组的父DOM元素.给定键的数据被分配给具有匹配键的元素.如果多个元素具有相同的键,则将重复的元素放入出口选择中; 如果多个数据具有相同的密钥,则将重复数据放入输入选择中.

在您的情况下,这将是关键功能:

.data(data, d => d.label)
//key-------^
Run Code Online (Sandbox Code Playgroud)

检查完全相同的代码,但具有关键功能:

var svg = d3.select("svg");

var data = [{
  label: 'a',
  value: 1
}, {
  label: 'b',
  value: 3
}, {
  label: 'c',
  value: 2
}];

var data2 = [{
  label: 'c',
  value: 99
}, {
  label: 'b',
  value: 2
}, {
  label: 'a',
  value: 3
}];

var xScale = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.value)])
  .range([0, 260]);

var yScale = d3.scaleBand()
  .domain(data.map(d => d.label))
  .range([10, 140])
  .padding(0.3);

var rects = svg.selectAll("foo")
  .data(data, d => d.label)
  .enter()
  .append("rect");

rects.attr("x", 40)
  .attr("y", d => yScale(d.label))
  .attr("height", yScale.bandwidth)
  .attr("width", d => xScale(d.value))
  .attr("fill", "teal");
  
d3.axisLeft(yScale)(svg.append("g").attr("transform", "translate(40,0)"))

d3.select("button").on("click", function() {

  xScale.domain([0, d3.max(data2, d => d.value)]);

  rects.data(data2, d => d.label);

  rects.transition()
    .duration(1000)
    .attr("width", d => xScale(d.value))

})
Run Code Online (Sandbox Code Playgroud)
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click</button>
<br>
<svg></svg>
Run Code Online (Sandbox Code Playgroud)

你可以看到,尽管新数据数组中的对象顺序不同(c第三个,现在c是第一个),这没关系,因为D3将数据绑定到每个DOM元素,同时考虑到该label物业.