D3.js, understanding Enter, Update and Exit

Enter, Update and Exit are essentially a way that D3 binds data to DOM elements such as SVG elements or any other HTML elements. 

Enter

Enter attempts to append a DOM element (such as an SVG element or any other HTML element specified in the selection), a number of times that matches a given number of data items, so that each data items gets assigned a representative DOM element. The number of DOM elements created by the action will be = (number of data items - number of existing DOM elements selected).

Update

Update, simply updates a number of DOM elements specified in the selection, that matches the number of supplied data items. Please note that Update does not have a named method, but is an operation you perform on the existing data.

Exit

In opposite to Enter, Exits attempts to remove any existing DOM elements specified in the selection such that the number of remaining DOM elements always matches the number of specified data items. Which means that if no data items are available or supplied, and one or more DOM elements specified in the selection are present, then Exits will remove all of the elements.

Hence Enter, Exits and Update when used together is the mechanism by which D3 binds DOM elements and their corresponding data items so that the DOM elements and data items are kept in sync as the number of data items enters and exists the HTML documents which D3 manages.

The following javascript code is an example that demonstrates this behaviour. The function named render is called a number of times with a varying number of data items as the first parameter and a colour as the second parameter. There is a time delay set between each call, so that you can see how D3 manipulates the DOM elements to match the data items.

<script src="~/Scripts/d3.min.js"></script>

<script language="javascript">

    var scale = d3.scale.linear()
      .domain([1, 5])   // Data space
      .range([0, 200]); // Pixel space

    var svg = d3.select("body").append("svg")
      .attr("width", 250)
      .attr("height", 250);

    function render(data, color) {

        var rects = svg.selectAll("rect").data(data);

        // Enter
        rects.enter().append("rect")
          .attr("y", 50)
          .attr("width", 20)
          .attr("height", 20);

        // Update
        rects
          .attr("x", scale)
          .attr("fill", color);

        // Exit
        rects.exit().remove();

    }

    setTimeout(function () { render([1, 2, 2.5], "red"); }, 1000);
    setTimeout(function () { render([1, 2, 3, 4, 5], "blue"); }, 2000);
    setTimeout(function () { render([1, 2], "green"); }, 3000);
    setTimeout(function () { render([3, 4, 5], "cyan"); }, 4000);
    setTimeout(function () { render([4, 5], "magenta"); }, 5000);

</script>
Run the code on JSFiddle or Github Gist

The above code will render a set of SVG rectangle DOM elements as follows.


















Note that each render will replace the previous render, so only one set of rectangles will be displayed at a time as they transit from render 1 to render 5. The data items at the top is to help in identifying the association between the DOM elements and the data items. Also note that although the shapes are squares, they are still SVG rectangle DOM elements with both the width and height set to 20 pixels.

The first render([1, 2, 2.5], "red"), will append 3 rectangles via the ENTER method to be bound to the 3 data elements supplied in the first parameter. The UPDATE will then update all available rectangles with red. The EXIT method as no effect since the number of data elements and the number of DOM elements are now in sync.

Notice how the number of data elements supplied is associated with the number of rectangles appended and the value of the data element is associated with the pixel space between the rectangles which was configured in the first line in the code. i.e the data domain 1 to 5 has been associated with the pixel range 0 to 200. Therefore each data element is worth 40 pixel spaces and half of any data element is worth 20 pixel spaces.

The second render([1, 2, 3, 4, 5], "blue"), will first append 2 rectangles via the ENTER method and then update all the 5 rectangle elements to blue via the UPDATE. Since the number of data elements and number of DOM elements are in sync there is no effect by the EXIT method.

The third render([1, 2], "green"), will have no impact by the ENTER method no new data elements have entered. Now there are altogether 5 rectangles, so the UPDATE will colour all of then green. However now the EXIT method have an impact as only 2 data elements are supplied but there are 5 rectangles available, hence the 3 additional once will be removed in order to keep the number of data elements and number of DOM elements in sync. Notice that the rectangles removed are the once that were not associated with the supplied data elements, i.e. 3, 4 and 5.

Now only 2 green rectangles associated with data elements 1 and 2 remain. Then the fourth render([3, 4, 5], "cyan"), will first append the 3 new data elements supplied via the ENTER method. The UPDATE will then colour all available rectangles with cyan. The EXIT method will then remove all rectangles other than the once associated with the newly supplied data elements 3, 4 and 5.

The fifth and final render([4, 5], "magenta"), will have no impact by the ENTER method as the rectangles associated with the supplied data elements 4 and 5 are already present as a result of the previous render. The UPDATE will as usual update all available rectangles with the supplied colour in the second parameter which in this instance is magenta. And finally the EXIT method will remove all rectangles not associated with the supplied data elements 4 and 5 leaving only the 2 magenta rectangles at the end of execution.



0 comments:

Post a Comment