D3 Data Updates
Tutorial
In the last lab I covered how to create new elements in your document from data points. In this one I'll explain how to handle data changes.
Why would my data change you ask? There are a couple reasons it might. One is that your visualization is interactive and what is displayed can be changed by the user. For example if your visualization shows only a part of the dataset to make it more manageable. Imagine a stock chart that goes back several years, users may only find it meaningful to look at chunks of this data at a time. If you had a scroll bar then you would have to update the displayed dataset. Another possiblilty is if your visualization displays more data about a particular data point when you select it or hover over it. In that case you would have to update the data for the "highlighted" data point. Finally some visualizations track processes that continue to generate data and update as the new data is generated. These visualizations need a way to update the dataset with the latest data and potentially remove data that has served it's purpose.
Last time we learned that enter() returns a selection of the data points that do not yet have document elments associated with them. The exit() function returns the elements in your document that no longer exist in your data set. To be able to do this D3 needs a way to uniquely identify your data points. If it can't tell your data points appart, how can it tell you which are the same before and after you change the data set? You provide the key to D3 by specifying an accessor function to D3 that returns a unique identifier. This is the second optional parameter of data(dataset,key). Typically once you have an exit selection, you call the remove() function on the selection, which deletes the document elements that are no longer in the data set. Let's look at an example.
First we create a list of fruit in a bowl.
var ex1Div = d3.select("#example1"); ex1Div.append("h3") .text("fruit in bowl"); ex1Div.append("ol") .selectAll("li") .data(["apple", "banana", "pear"]) .enter() .append("li") .text(function(d) { return d; });
Next, we'll remove the apple.
ex1Div.selectAll("li") .data(["banana", "pear"], function(d) { return d; }) .exit() .remove();
Note how we pass the second parameter to data() before we call remove. D3 uses this function to tell that there is an element in the document that is bound with the data for "apple". D3 compares keys from the dataset to the data items bound to elements of the document. Based on this D3 knows which elements have no corresponding data point in the dataset.
By default D3 uses the position of the item in the dataset as the key. You can ignore this detail only if you do not want to update your dataset. If D3 is only adding new elements, there is no need for it to be able to tell elements apart. However, if you are updating data you almost never want the default behavior. Here is what we would get if we did not specify the key to D3.
ex1Div.selectAll("li") .data(["banana", "pear"]) .exit() .remove();
Because we did not specify a key D3 used each data point's position in the dataset as the key. In effect D3 "sees" the inital dataset as [1,2,3] before removing apple and [1,2] after. D3 then correctly decides that 3, which corresponds to pear, no longer exists in the dataset. Be wary and always specify a key when updating data!
A quick recap on data(), enter(), and exit()
The enter() selection exists so that you can add elements to your document that hasn't already been included. Likewise the exit() selection exists so that you can remove elements from your document that are no longer in your dataset. In most cases you call enter() followed by append(), which will create a new element for each new data item. In most cases you call exit() followed by remove() which will remove elements that are no longer in the dataset.
Quiz
Which method most often goes with exit()? - answer remove()
Which method most often goes with enter()? - answer append()
Specifying a key is only necessary when using exit(). True/False
Things to do
In the lab below there is a partially implemented shopping list. You can enter text and press "Add to List" to add an item to the list.
- Implement the "Remove Last" button. When clicked it should delete the most recently added item to the list. Note, to remove the last item from a Javascript array use the pop() method, it will return the last item and change the array so that it no longer contains the last item.
- Change "Remove Last" to a "Remove Item" button. It should now be a text input that takes a list item number, and when the "Remove Item" button is pressed it will delete the list item with that number, or if the item does not exist, it does nothing. Note, to delete an array item from an arbitrary position use the splice() method with two parameters. The first is the position to start deleting from and the second is how many items to delete. Remember that the first element in the array is at position 0!
var a = [1,2,3]; var popped = a.pop(); // a === [1,2] // popped === 3
var arr = ["a","b","c"]; var removed = arr.splice(1,1); // arr = ["a","c"]; // removed === "b"
Extra Credit
- Determine if it makes a difference what order enter() and exit() are called in.
- Intentionally use data() without the key parameter, see how it affects the behavior.