官术网_书友最值得收藏!

Binding an array as data

One of the most common and popular ways to define data in D3 visualization is through the use of JavaScript arrays; for example, say you have multiple data elements stored in an array, and you want to generate corresponding visual elements to represent each and every one of them. Additionally, when the data array gets updated, you would want your visualization to reflect such changes immediately. In this recipe, we will accomplish this common approach.

Getting ready

Open your local copy of the following file in your web browser:

https://github.com/NickQiZhu/d3-cookbook-v2/blob/master/src/chapter3/array-as-data.html .

How to do it...

The first and most natural solution that might come to mind is iterating through the data array elements and generating their corresponding visual elements on the page. This is definitely a valid solution, and it will work with D3; however, the enter-update-exit pattern we discussed in the introduction provides a much easier and more efficient way to generate visual elements. Let's take a look at how we can do that:

var data = [10, 15, 30, 50, 80, 65, 55, 30, 20, 10, 8]; // <- A 
    function render(data) { // <- B 
        var bars = d3.select("body").selectAll("p.h-bar") // <- C 
                .data(data); // Update <- D 
        // Enter 
        bars.enter() // <- E 
                .append("p") // <- F 
                    .attr("class", "h-bar") // <- G 
            .merge(bars) // Enter + Update <- H 
                .style("width", function (d) { 
                    return (d * 3) + "px"; // <- I 
                }) 
                .text(function (d) { 
                    return d; // <- J 
                }); 
        // Exit 
        bars.exit() // <- K 
                .remove(); 
    } 
    setInterval(function () { // <- L 
        data.shift(); 
        data.push(Math.round(Math.random() * 100)); 
        render(data); 
    }, 1500); 
    render(data); 

This recipe generates the following visual output:

Data as array

How it works...

In this example, data (a list of integers in this case) is stored in a simple JavaScript array as shown on the line A in the preceding code. The render function is defined on the line marked as B so that it can be repeatedly invoked to update our visualization. The selection starts on the line marked as C, which selects all p elements on the web page with the h-bar CSS class. You are probably wondering why we are selecting these p elements since they don't even exist on the web page yet. This is in fact true; however, the selection at this point is used to define the visual set we discussed in the introduction. By issuing this selection, that we made in the previous line, we essentially declare that there should be a set of the p.h-bar elements on the web page to form our visual set. On the line marked as D, we invoke the data function on this initial selection to bind the array as our dataset to the to-be-created visual elements. Once the two sets are defined, the enter() function on line E can be used to select all pieces of data elements that are not yet visualized. When the render function is invoked for the very first time, it returns all elements in the data array, as shown in the following code snippet:

        var bars = d3.select("body") 
                       .selectAll("p.h-bar") // <- C 
                       .data(data); // Update <- D 
        // Enter 
        bars.enter() // <- E 
            .append("p") // <- F 
            .attr("class", "h-bar") // <- G 

On line F, a new p element is created and appended to the body element of each data element selected in the enter function; this essentially creates one p element for each datum. Finally, on line G, we set its CSS class to h-bar. At this point, we basically created the skeleton of our visualization, including the empty p elements. Next step is to change the visual attributes of our elements based on the given data.

Tip

D3 injects a property to the DOM element named __data__ to make data sticky with visual elements so when selections are made using a modified dataset, D3 can compute the difference and intersection correctly. You can see this property easily if you inspect the DOM element either visually using a debugger or programmatically.

As illustrated by the preceding screenshot, this is very useful to know when debugging.

In the following code snippet, on line H, the merge function is invoked with the selection as its parameter. This function call essentially merges the enter selection with the update selection and returns the union of both selections, thus allowing us to chain modifiers for both enter and update scenarios. Without the merge function, we would need to repeat the code here for both enter and update scenarios. Then, on line I, we applied a dynamic style attribute width to be three times the integer value associated with each visual element, as shown in the following code snippet:

        bars.enter() // <- E 
                .append("p") // <- F 
                    .attr("class", "h-bar") // <- G 
            .merge(bars) // Enter + Update <- H 
                .style("width", function (d) { 
                    return (d * 3) + "px"; // <- I 
                }) 
                .text(function (d) { 
                    return d; // <- J 
                }); 

All D3 modifier functions accept this type of dynamic function to compute its value on the fly. This is precisely what it means to data drive your visualization. Hence, it is crucial to understand what this function is designed to achieve in our example. This function receives a parameter d, which is the datum associated with the current element. In our example, the first p bar has the value 10 associated as its datum, the second bar has 15, and so on. Therefore, this function essentially computes a numeric value that is three times the datum for each bar and returns it as the element's width in pixels. While, on line J, we used a similar approach to change the text content of the p element to the datum value associated with each element.

Note

The dynamic modifier function actually accepts two parameters, d and i. The first parameter d is the associated datum we just discussed, and i is a zero-based index number for the current element. Some recipes in the previous chapter relied on this index, and in the rest of this chapter, we will take look at other recipes that utilize this index in different ways.

The following is the raw HTML code that resulted from this update process:

<p class="h-bar" style="width: 30px;"> 
    10 
</p> 
<p class="h-bar" style="width: 45px;"> 
    15 
</p> 
.... 
<p class="h-bar" style="width: 24px;"> 
    8 
</p> 

The following last section, Exit section, is fairly simple:

bars.exit() // <- K 
    .remove(); 
Note

The selection returned by the exit() function is just like any other selection. Therefore, although remove is the most common action used against the exit selection, you can also apply other modifiers or transitions to this selection. We will explore some of these options in later chapters

On line K in the preceding code snippet, the exit() function is called to compute the set difference of all visual elements that are no longer associated with any data. Finally, the remove() function is called on this selection to remove all the elements selected by the exit() function. This way, as long as you call the render() function after you change our data, you can always ensure that our visual representation and data are kept synchronized.

Now, let's implement the following last block of code as follows:

setInterval(function () { // <- L 
        data.shift(); 
        data.push(Math.round(Math.random() * 100)); 
        render(data); 
    }, 1500); 

On line L, a simple anonymous function was created to remove the top element in the data array using the shift function while appending a random integer to the data array using the push() function every 1.5 seconds. Once the data array is updated, the render() function is called again to update our visualization to keep it synchronized with the new dataset. This is what gives our example its animated bar chart look.

主站蜘蛛池模板: 肥城市| 宁明县| 宽甸| 上杭县| 松溪县| 东辽县| 海安县| 隆化县| 信阳市| 锡林浩特市| 邵阳市| 郸城县| 鄂伦春自治旗| 浮梁县| 章丘市| 集安市| 富顺县| 阿鲁科尔沁旗| 兴安县| 临海市| 澄城县| 乐陵市| 涞源县| 绥宁县| 临漳县| 长顺县| 曲阳县| 蚌埠市| 安阳县| 基隆市| 南靖县| 瓮安县| 松原市| 唐海县| 沭阳县| 大田县| 绍兴市| 尚志市| 新田县| 兴仁县| 商洛市|