D3.js 是否使用.update、.enter和.exit更新条形图?
我做了一个简单的条形图。我想通过在数据的不同子集之间切换来更新条形图(动态)。据我所见,似乎有两种方法:D3.js 是否使用.update、.enter和.exit更新条形图?,d3.js,D3.js,我做了一个简单的条形图。我想通过在数据的不同子集之间切换来更新条形图(动态)。据我所见,似乎有两种方法: 绘制图表,然后在按钮上单击(或其他)删除整个图表并重新绘制 使用不同的数据选择,通过单击按钮从图表中“更新”、“输入”和“退出”条形图 我正在尝试选择2。这是我的密码: // set dimensions var margin = {top: 10, right: 20, bottom: 50, left: 100}; var height = 600 -margin.top - margi
// set dimensions
var margin = {top: 10, right: 20, bottom: 50, left: 100};
var height = 600 -margin.top - margin.bottom;
var width = 600 - margin.left - margin.right;
// set ranges
var x = d3.scale.linear().range([width, 0]);
var y = d3.scale.ordinal().rangeRoundBands([0, height], .05);
// define y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// add svg container
var svg = d3.select("#chart")
.append("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// load data
d3.json("data.json", function(data) {
// format data
data.forEach(function(d) {
d.enrols = +d.enrols;
});
// register click event
d3.selectAll(".toggle").on("click", function() {
render(this.id);
});
// initialise
render("group-1");
function render(selection) {
// filter data
var subset = data.filter(function(d) { return d.group === selection} );
// scale range of data
x.domain([0, d3.max(subset, function(d) { return d.enrols; })]);
y.domain(subset.map(function(d) { return d.month; }));
// update selection
var bars = svg.selectAll(".bars")
.data(subset, function(d) { return d.month; });
// add bars
bars.enter()
.append("g")
.attr("class", "bar")
.attr("transform", function (d, i) {
return "translate(0," + y(d.month) + ")";
});
bars.append("rect")
.attr("height", y.rangeBand())
.attr("width", function (d) {
return width - x(d.enrols);
});
bars.append("text")
.attr("x", function (d) {
return width - x(d.enrols) - 15;
})
.attr("y", y.rangeBand() / 2)
.attr("dy", ".35em")
.text(function (d) {
return d.enrols;
});
bars.exit().remove();
// add y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
}
});
问题是我的旧数据从未退出,新条被附加,但旧条仍然存在。我读过d3选择的理论,但我发现它们很难在实践中实现
这是一本书。这里是。很好,您选择了第二个选项而不是第一个选项:删除所有元素并再次绘制图表是一种非常懒惰的编码方式 但是,在您的情况下,由于数据的长度不变,因此不需要“输入”、“更新”和“退出”选项 如果将一些不变的部分移到
渲染
函数之外,则一切都会变得更简单:
y.domain(data.map(function(d) {
return d.month;
}));
var bars = svg.selectAll(".bars")
.data(data, function(d) {
return d.month;
}).enter()
.append("g")
.attr("class", "bar")
.attr("transform", function(d, i) {
return "translate(0," + y(d.month) + ")";
});
var rects = bars.append("rect")
.attr("height", y.rangeBand())
.attr("width", function(d) {
return width - x(d.enrols);
});
var texts = bars.append("text")
.attr("x", function(d) {
return width - x(d.enrols) - 15;
})
.attr("y", y.rangeBand() / 2)
.attr("dy", ".35em")
.text(function(d) {
return d.enrols;
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
只保留真正改变的东西:
- 你的x标度范围
- 绑定到组的数据
- 矩形和文本的过渡
function render(selection) {
// filter data
var subset = data.filter(function(d) {
return d.group === selection
});
// scale range of data
x.domain([0, d3.max(subset, function(d) {
return d.enrols;
})]);
bars.data(subset, function(d) {
return d.month;
});
rects.transition().duration(500).attr("width", function(d) {
return width - x(this.parentNode.__data__.enrols);
});
texts.transition().duration(500).attr("x", function(d) {
return width - x(this.parentNode.__data__.enrols) - 15;
})
.text(function(d) {
return this.parentNode.__data__.enrols;
});
}
这是你的密码笔:
PS:如果你去掉这些组,直接添加矩形和文本,这会更容易。你的条形图只需要非常简单的修改。以下是缺失的拼图: 当我们使用
data()
函数时,D3会在现有DOM元素和您的数据值之间执行转换。将此操作的结果存储在变量栏上
。我们可以在条上调用多个方法
方法exit()
返回要删除的元素。如果DOM上有3个条,并且我们使用一个只有2个数据值的数组调用data()
函数,exit()
将返回将被删除的条
同样,方法enter()
返回要添加的元素。如果我们在DOM上有2个条,并且调用带有3个数据值的数组的data()
函数,enter()
将返回要添加的新条
那么,将要更新的条形图呢?它们是存储在条中的变量
将其转换为代码非常简单:
这:
变成这样:
var newBars = bars.enter()
.append("g")
...
newBars.append(...)
然后每次出现时:
bars.append(...)
变成这样:
var newBars = bars.enter()
.append("g")
...
newBars.append(...)
每次有一个新的条,我们就向DOM追加一些东西
每次我们需要更新一个条时,我们都从DOM中选择一些内容
条
指将要更新的条。因此,我们添加此代码以更新现有条形图:
bars.select('rect')
.attr("width", function (d) {
return width - x(d.enrols);
});
bars.select('text')
.attr("x", function (d) {
return width - x(d.enrols) - 15;
})
.text(function (d) {
return d.enrols;
});
selectAll(“.bar”)
是错误的,因为没有具有类bar
的元素,只有具有类bar
的元素。您正在创建多个轴。您还向同一组g
元素添加了多个条。看看代码笔中生成的SVG。你的意思是:?似乎没用?很好!您几乎解决了代码笔中的所有问题。每次调用render
函数时,您仍在向图表中添加一个新轴。你应该解决这个问题。在任何情况下,我现在都可以向您解释d3的数据联接。this.parentNode.\uuuu data\uuuuu.enrols
?不d.注册
?不,不d
,除非我们在这里创建“输入”、“更新”和“退出”选项,这正是我要避免的。谢谢。这对我来说是一个学习练习-因此,如果数据的长度发生了变化,即我在第二个数据子集中没有11月和12月的数据,该怎么办?在这种情况下,您需要选择“输入”、“更新”和“退出”(顺便说一句,没有组更容易).我遵循您的逻辑:条
保存连接的dom元素和数据;将要添加的新条存储在变量中,无需删除任何内容,只需使用selectAll
更新即可。所有我得到的是,虽然我的酒吧宽度增加了一点,每切换??更新代码笔:对不起,我弄错了。在我写的selectAll
中,应该是select
。我更新了我的答案。顺便说一句,D3V3和D3V4中的数据联接有一些不同。如果你对理解这些差异感兴趣,看看是什么。