# CS465 - Lab 7 Update, Enter, Exit

# Objectives

  • Get some practice working with update, enter and exit selections

# Starting the lab

Go to Github Classroom and grab the starter code for the lab.

# Welcome to the graveyard

In honor of Halloween, we are going to use our visualization tools to make something... a little different. If you open ghosts.html in your browser, you will be greeted by a mostly black screen. Your task is to get the ghosts to come out an play.

Take a moment to read through the source code. You can see that I have things set up a little bit differently. The important thing to take away from this is that we have an array called ghost_data, that we populate with objects that have properties x, y, and name (see the add_ghost() function which creates new random ghost records). This is our "data".

We also have a function called spook_ghosts(), which changes the location of the ghosts to some new random location.

Finally, note that we have an event handler that adds a new ghost whenever the "Add a ghost" is clicked.

At the moment, however, no matter what you do, no ghosts appear. Careful examination of the code should lead you to update_ghosts(), which currently has no code in it...

# Adding ghosts

The first thing we will add to update_ghosts is a way to create new ghosts. We will follow the same pattern we have done in other labs and assignments.

Step one Select all ghosts and do a data binding. Use our normal selectAll(".ghost") pattern to grab everything with the class "ghost" (which should be an empty set right now). Use .data() to bind ghost_data to the selection. Save this in a variable called ghosts (like the circles in past labs).

Step two Create new ghosts. Using your selection, use .enter() and .append() to create new ghost objects. For the ghosts, we will append new tags of type "image".

Step three Give the ghost attributes. For x and y, use the values from the data (using an accessor function). For width and height, use GHOST_SIZE. To load the image use .attr("xlink:href", "ghost.svg").

Step four Test. Reload the page, and some ghosts will appear. Click the button to add more to the scene.

Step five Make the ghosts fade in. Use style to give the ghosts an "opacity" of 0. We will use a transition to fade them in. Using your ghosts variable, create a transition. Set a duration and a delay. I gave mine a duration of (d)=>rand_int(3000)+2000 and a delay of (d)=>rand_int(1000). Adjust to your taste. Note that I use accessor functions despite the fact that I don't make use of the d (or i). This just means that each ghost has a unique delay and duration. Finally, use .style() to set the "opacity" to 1.

Step six Test. You should see the ghosts fade in.

# Scatter the ghosts

If you look in the event handler, you can see that there is a line commented out. When a new ghost arrives, the other ghosts are started and should race about a bit. However, if you uncomment the line, you will see that they don't really do much scattering.

The reason for this is because we are currently working with the enter selection. We never update the positions of the update selection (those data elements that already had a binding to a visual element on the page). In this case, we could deal with the enter and the update selections completely separately. However, we are going to follow a common pattern. We will deal with the adding of new elements and basic initialization with the enter selection, and then we will merge the enter and update selections to perform actions shared by both of them.

Step one Create a new variable to hold the enter set. When you first do your data binding, your first line should look like let ghosts = svg.selectAll(".ghost").data(ghost_data);. This way, our variable ghosts has access to all three selections. Then, create a new variable called new_ghosts. Set this equal to ghosts.enter(). This is now the enter selection. You should chain on the append and other initialization functions that set position, image and initial opacity.

Step two Merge the two selections. Before you do the transition, add a line that says ghosts = ghosts.merge(new_ghosts);. The merge() function combines in a second selection and returns a new selection that is the combination.

Step three Make the old ghosts move. The transition now should affect all ghosts, but the ghosts still don't move. That is because the only thing the transition does is set the opacity. Add att() lines to set x and y using accessor functions.

Step four Test. The ghosts should all glide around when a new ghost is added.

# Retire ghosts

Sometimes ghosts just get tired of haunting and just fade away. We are going to change our ghosts so that if they are clicked on they will just fade away.

Step one Add a click handler to new_ghosts. We only need to register event handlers once, so we will put this onto the enter selection new_ghosts. We will register a handler for the "click" event.

Step two Remove the ghost from the data. The first argument to the event handler is the data associated with the clicked object. Call this item_data. We want to remove this from our ghost_data array. A quick approach would be ghost_data = ghost_data.filter((d)=>d !== item_data).

Step three Call update_ghosts(). The data has changed, so we want to make sure to update the visuals.

Step four Actually remove the ghost. If you test at this point, you will find that ghosts don't really disappear so much as they fly over and combine with a different one. This is because we aren't removing the ghosts that no longer have data to map to. We need to make use of the exit set. We typically deal with the exit set before the other updates because we have so little to do. So, immediately after the line that does the data binding, call ghosts.exit(). This returns the exit set. Calling remove() on this removes all of the orphaned DOM elements from the page.

Step five Make the removal more dramatic. To make the ghost fade away, put a transition with a duration between the exit() and the remove(). Add another line before the remove() that sets the opacity to 0.

Step six Make the right ghost disappear. If you test this, you will find that sometimes the wrong ghost leaves. This is because D3 is doing the binding from scratch. If we remove a ghost from the middle of the data list, the next time we do a data binding, the ghosts will potentially be bound to different data. We would like to enforce data constancy. which means the data is always bound to the same object. We do this by adding a second argument to the data() function. This second argument is called the key function. D3 will use this to make sure that the same data is bound to the same item. This is why I gave all of the ghosts unique ids. Add in a key function that returns the ghost's name.

Step seven Test, test, test (and have a good Halloween).

Last Updated: 10/31/2018, 1:12:13 PM