select
从其数据绑定中选择了 d3.js,则该子元素将继承子元素。如何对多个子对象执行相同的方法-即在父对象上使用selectAll
,并将数据传播到该子对象选择中?我尝试在子项选择上使用datum(function(d) {return d;})
,但是似乎只能使用一次,并且在尝试更新时由于某种原因会返回旧数据。
我可以通过为每个孩子循环使用select
来解决此问题,但是我很乐意找到一种更优雅的方法。
在这个小提琴中,左边的不是有效的,而是所需的解决方案,右边是有效的但较难看的解决方案。按下按钮以查看不同的行为
var oldData = [{
x: 0,
y: 0
}, {
x: 10,
y: 0
}];
var newData = [{
x: 0,
y: 0
}, {
x: 10,
y: -5
}];
var lineGen = d3.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
function case1(data) {
var sel = d3.select('g.c1')
.selectAll('g.connection')
.data(data);
sel.exit().remove();
var selEnter = sel.enter()
.append('g').classed('connection', true);
selEnter.append('path').classed('real', true);
selEnter.append('path').classed('mouse-capture', true);
selEnter.merge(sel)
.selectAll('path')
.datum(function(d) {
return d;
})
.attr('d', function(d) {
return lineGen(d);
});
}
function case2(data) {
var sel = d3.select('g.c2')
.selectAll('g.connection')
.data(data);
sel.exit().remove();
var selEnter = sel.enter()
.append('g').classed('connection', true);
selEnter.append('path').classed('real', true);
selEnter.append('path').classed('mouse-capture', true);
sel = selEnter.merge(sel);
sel.select('path.real')
.attr('d', function(d) {
return lineGen(d);
});
sel.select('path.mouse-capture')
.attr('d', function(d) {
return lineGen(d);
});
}
function setNewData() {
case1([newData]);
case2([newData]);
}
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 100)
.attr('viewBox', '-5 -10 40 20');
svg.append('g')
.classed('c1', true);
svg.append('g')
.classed('c2', true)
.attr('transform', 'translate(20,0)');
case1([oldData]);
case2([oldData]);
body {
margin: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
path.real {
stroke-width: 1px;
stroke: black;
}
path.mouse-capture {
stroke-width: 5px;
stroke: black;
opacity: 0;
}
g:hover path.real {
stroke: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</head>
<body>
<button onclick="setNewData()">new data</button>
</body>
答案 0 :(得分:2)
很遗憾,除非您更改了d3-selection
源代码,否则您将无法实现所需的功能。
如您所知,selectAll
与select
不同,它不会传播数据。为了让其他读者也理解这个问题,以下是我制作的表格,该表格总结了select
和selectAll
之间的区别:
+------------------+----------------------------------+----------------------------+
| Method | select() | selectAll() |
+------------------+----------------------------------+----------------------------+
| Selection | selects the first element | selects all elements that |
| | that matches the selector string | match the selector string |
+------------------+----------------------------------+----------------------------+
| Grouping | Does not affect grouping | Affects grouping |
+------------------+----------------------------------+----------------------------+
| Data propagation | Propagates data | Doesn't propagate data |
+------------------+----------------------------------+----------------------------+
因此,您的function case1
方法是在艰辛的环境中,因为:
selectAll
,它将不会传播数据; selectAll
与datum
一起使用,它将只访问所选元素的旧基准(如您所发现的那样); selectAll
与data
一起使用,则返回原点
包裹在一个数组中,它将仅返回一次数据 (在您的情况下,仅针对第一个路径),这是预期的,因为它是组的父数据。如果将console.log(i)
放在datum(d,i)
内,则会看到i
始终是0
。但是,出于好奇, 是 ,它可以在function case1
中完成您想要的操作,但是比function case2
丑陋(顺便说一下,这是惯用的D3):直接从父级获取数据。
赞:
.datum(function(d) {
return d3.select(this.parentNode).datum();
})
这是演示:
var oldData = [{
x: 0,
y: 0
}, {
x: 10,
y: 0
}];
var newData = [{
x: 0,
y: 0
}, {
x: 10,
y: -5
}];
var lineGen = d3.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
function case1(data) {
var sel = d3.select('g.c1')
.selectAll('g.connection')
.data(data);
sel.exit().remove();
var selEnter = sel.enter()
.append('g').classed('connection', true);
selEnter.append('path').classed('real', true);
selEnter.append('path').classed('mouse-capture', true);
selEnter.merge(sel)
.selectAll('path')
.datum(function(d) {
return d3.select(this.parentNode).datum();
})
.attr('d', function(d) {
return lineGen(d);
});
}
function case2(data) {
var sel = d3.select('g.c2')
.selectAll('g.connection')
.data(data);
sel.exit().remove();
var selEnter = sel.enter()
.append('g').classed('connection', true);
selEnter.append('path').classed('real', true);
selEnter.append('path').classed('mouse-capture', true);
sel = selEnter.merge(sel);
sel.select('path.real')
.attr('d', function(d) {
return lineGen(d);
});
sel.select('path.mouse-capture')
.attr('d', function(d) {
return lineGen(d);
});
}
function setNewData() {
case1([newData]);
case2([newData]);
}
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 100)
.attr('viewBox', '-5 -10 40 20');
svg.append('g')
.classed('c1', true);
svg.append('g')
.classed('c2', true)
.attr('transform', 'translate(20,0)');
case1([oldData]);
case2([oldData]);
body {
margin: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
path.real {
stroke-width: 1px;
stroke: black;
}
path.mouse-capture {
stroke-width: 5px;
stroke: black;
opacity: 0;
}
g:hover path.real {
stroke: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</head>
<body>
<button onclick="setNewData()">new data</button>
</body>