Re: How to redraw the view when the new array has less element

2,348 views
Skip to first unread message

Chris Yang

unread,
Jun 15, 2011, 12:53:38 AM6/15/11
to d3-js
I'm confused about how to change the data and the view when the the
number of element in the new data array is different from the original
one.

I'm trying to redraw a bar chart view after updating the new data
array. But as the number of the new array is less than the old one. so
I got a few old bars left. I don't know how to remove them.

Do you guys have any idea? Thanks in advance.

Here is the code.

<code>

<html>

<head>
<title>D3 html demo page</title>
<script type="text/javascript" src="d3.js"></script>
<style type="text/css">

.chart rect {
fill: steelblue;
stroke: white;
}

</style>
</head>

<body>

<script type="text/javascript">
var w = 20, h = 80;
var t = 1297110663, // start time (seconds since epoch)
v = 70, // start value (subscribers)
data = d3.range(33).map(next); // starting dataset

function next() {
return {
time: ++t,
value: v = ~~Math.max(10, Math.min(90, v + 10 * (Math.random()
- .5)))
};
}


//customized function command

var x = d3.scale.linear()
.domain([0, 1])
.range([0, w]);

var y = d3.scale.linear()
.domain([0, 100])
.rangeRound([0, h]);


var chart = d3.select("body")
.append("svg:svg")
.attr("id","sb")
.attr("class", "chart")
.attr("width", w * data.length - 1)
.attr("height", h);


var rect = chart.selectAll("rect")
.data(data);
rect .enter().append("svg:rect")
.attr("x", function(d, i) { return x(i) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); })
;

chart.append("svg:line")
.attr("x1", 0)
.attr("x2", w * data.length)
.attr("y1", h - .5)
.attr("y2", h - .5)
.attr("stroke", "#000");


function mf(){

//rect.exit().remove();
data = d3.range(20).map(next);
chart.selectAll("rect")
.data(data)
.attr("x", function(d, i) { return x(i) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); });
}

</script>


<input type="button" value="click" onclick="mf();"></input>

</body>

</html>
</code>

Mike Bostock

unread,
Jun 15, 2011, 12:06:15 PM6/15/11
to d3...@googlegroups.com
> I got a few old bars left. I don't know how to remove them.

Short answer:

var children = parent.selectAll("child").data(newData);
children.exit().remove();

Or, in terms of your code, in the mf() function, you'll want to save
the result of the `data` operator as a local variable, say `var rect`.
Then, after you're done applying operators to the updating selection
(i.e., the `rect` selection), you'll want to access the exiting
selection (`rect.exit()`) and then remove those nodes. You might also
need to handle the entering selection (`rect.enter()`) and append new
nodes if needed.

Narrative answer:

http://mbostock.github.com/d3/tutorial/bar-2.html

Technical answer:

https://github.com/mbostock/d3/wiki/Selections#data

Mike

Chris Yang

unread,
Jun 15, 2011, 9:10:26 PM6/15/11
to d3-js
Thanks for Mike's answer.

I did try to use the similar code to remove the bar. As you can see in
the code above, the line in the "mf" function
//rect.exit().remove();
which has been commented for it doesn't work.

After tested for times, the following code without "exit" works well.

function mf(){

<b>chart.selectAll("rect").remove();</b>
data = d3.range(20).map(next);
chart.selectAll("rect")
.data(data)
.enter().append("svg:rect")
.attr("x", function(d, i) { return x(i) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); });



}

I am not sure why it can work in this way as it doesn't have a
"exit()" part.

Chris


Mike Bostock

unread,
Jun 18, 2011, 10:15:21 PM6/18/11
to d3...@googlegroups.com
> I am not sure why it can work in this way as it doesn't have a "exit()" part.

The exit selection doesn't remove the nodes; that's what the remove operator does. This is discussed at length in the documentation I linked in my previous email.

The code you sent works because you are removing all the elements, then reselecting, and thus you only have entering nodes. However, this is inefficient. It's better if you use the data operator, and then handle the enter, update and exit as three different cases.

In particular, you'll need to change the code so that you store a var as the result of the data operator. Then you can access the enter and exit selections. The standard form is:

var foo = parent.selectAll("foo").data(data);
foo.enter().append("foo")...
foo.exit().remove();
foo... // update here

So, you can't call exit() on the enter() selection, because the exit selection is defined on the result of the data operator only.

Mike

Reply all
Reply to author
Forward
0 new messages