[UPDATE] After another test, it turns out my understanding about key is wrong, the way key matching works is:
When D3 tries to match DOM and data, it will use that key function to extract identifier,
from DOM, it takes out the data value and use that value(could be object) to generate key for DOM element( if the key function use that Data, otherwise just run that key function and give a key value not matter what it is), I used to think it just store another attribute as key inside DOM element and just simply use that value as key, but actually it apply that key function and calculate the key based on current bind data.
from new Data, it directly use each datum to generate the key, and similar like from DOM.
And if the calculated key match each other, then nothing touched on this element, after all matching if can not find the match, then either put DOM into exit() or put data into update(with a plce holder for that going-to-appended DOM).
I have add this similar code here to facilitate others to understand this. The basic diff is this time I use array of object which has consist attribute which can be used as key function to generate key
function addbtn(){
var keys = [];
var data = [
{
id:0,
value:"a"
},
{
id:1,
value:"b"
},
{
id:2,
value:"c"
},
{
id:3,
value:"d"
}
]
var btns = d3.select(".body")
.selectAll("button")
.data(data, function(d, i){
// D3 will take out __data__ from each element, and use its __data__.id as key,
// same to the data to be bind, it takes each datum from that data array
// and use its id as key to match element's key if matched, go to next, until either there is data left or element left.
// So when I remove button 2, the comparing will find the element with data id 2 missing( there is data left),
//so it will put a place holder for an element which will bind the data 2
return d.id;
});
btns.enter()
.append("button")
.text(function(d, i){
return d.value;
});
btns.exit().remove();
}
I'm pretty new to the D3 key function as explained here
When I try some code like this:
<button onclick="addbtn()">Add New Button</button>
<div class="body"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.14/d3.js"></script>
<script type="text/javascript">
function addbtn(){
var keys = [];
var btns = d3.select(".body")
.selectAll("button")
.data([0,1,2,3], function(d, i){
var k = i;
keys.push(k);
return "key_"+k;
});
btns.enter()
.append("button")
.text(function(d, i){
return d;
});
btns.exit().remove();
}
</script>
My understanding about key is that it's a value which binds to both data and DOM elements to make them to be comparable. If the data and element have same key value (through a certain comparison algorithm), then they are matched and bound. (This is how I think it works but I'm not sure if my understanding is correct or not).
In my experiments, I have specified the key which returns the "key_"+index. To test:
Could anyone help with how to make D3 work like what I expect? I do not want to update anything inside the existing "update collection" that D3 turns into button 0 1 2, but rather I just want to add a missing element, inserting it as button 2.
You're using only the index as the key, and that changes across the different invocations. In particular, the list of indices will not ever have holes. So for your second call, the button with data 3 has index 2 and there's no index 3 as you've removed one button. So it's index 3 that isn't matched.
To make it work, use the actual data in the key function:
.data([0,1,2,3], function(d) { return d; })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With