Complex Infographics with d3.js in Repods — Part II

Adding Interactivity

Record Evolution
9 min readJul 30, 2018

Author: Justina Dešriūtė, Full-Stack Developer @Record Evolution GmbH

This is Part II of our two-piece article Interactive Infographics with d3.js in Repods. In what follows, we are going to demonstrate how to add interactivity to a complex infographic in the Repods data science platform.

The full code for this infographic can be found under this gist file.

Table of Contents

Choosing the Data

First, we need to select the data to be used inside the infographic. The smallest necessary dataset could already be provided through the report data if the data had been filtered in the SQL code panel in the Pipes tab to show only the information about the Top 9 Teams according to the FIFA rank:

SQL Code Panel

At times, we may have to transform the data with plain JavaScript in the D3 panel. Hence, we find it useful to demonstrate that JavaScript transformations on the data object are also easily performed.

As mentioned, the dataset from our report in this particular case comes in two arrays. We are only interested in the second one. We can access it using this syntax: var teamInfo = REP_Fifa_data.data. As we are only concerned with the top nine teams, we use the .filter() method to rank the teams, according to their FIFA rank, which is the third property of each team Object in our dataset: var top9 = teamInfo.filter(rank => rank[2] < 10). So now we have filtered the data to give us only arrays containing information about the top nine teams.

Preparation of the SVG Panel

The second major step is a platform-specific one — all the parts of an SVG element have to be appended to an element called REframe. This element is provided by Repods. The REframe element is a d3 selection consisting of a single group element <g> and has to be referred to performing selections within the code in this panel, e.g. REframe.selectAll(“td.data”), REframe.selectAll(“path”).

Adding the Balls, Representing the Teams

The first part can be divided into several stages. To begin with, the SVGs of the balls are added using an .append() method and linking the code of the SVG (via CSS selector ‘#ball’) using an ‘xlink:href’ attribute:

Initially, it is necessary to apply a transformation to scale the balls up or down to the desired size and select the location on the canvas where they should be placed with the translate attribute. The last line of code in the picture below (second transformation) is important for positioning the balls relative to each other. By changing the number, you can place them closer or further from one another. Then we must provide d3 with the dataset that is going to be used in the visualization by using .data() and .enter()methods on our dataset — top9. Besides, we are also giving each ball a class “team” which will be used later to select the elements. The main variable “teamG”, which will be heavily used in this infographic, is defined by telling d3 to select all the “g” elements having a class “team” — meaning, selecting all of the balls.

For example, one of the simplest actions performed on this element is selecting the team name as a text tag just beneath the ball to distinguish the teams one from another:

To do this, we have to add all text elements with .append(), set the “y” attribute to a certain number, depending on where on the y-axes we want to place it, and use a .text() method to set the text to the name of the team. As the team name for each team in our dataset is in the second position in the array, we grab it by the index “d[1]”.

Besides, each ball is colored differently. To achieve this, we are creating a variable teamColor() that internally uses a d3’s .scaleOrdinal() method. scaleOrdinal() requires a domain and a range. In our example, we give the letters as the domain, representing the groups. For the range, we provide an array of different colors expressed in HEX color codes.

Once teamColor is ready to use, we call the function as an argument in setting the “fill” attribute for each path tag in the balls’ SVG.

The teamColor function allows us to color all teams belonging to a given group in the same color:

Setting Up Interactivity

First, we are matching each ball with a set of data containing information about a particular team. Then the information is assigned to the <g> element. The <g> element consists of an image of a ball, a text underneath the image, and the information that will be displayed in the statistics table.

Initially, we check the length of the data that is passed into the click-on function. If the length is equal to 7, the first element of an array is removed using the .shift() method. This operation is performed because our data array contains an element that is not needed for this particular visualization — the date timestamp. Regular JavaScript methods (.shift(), ,filter(), .map(), .split(), .splice() etc.) can be used to modify the data arrays.

Further, we are selecting all the table cells with a class “data” — REframe.selectAll(“td.data”) — and assigning them the remaining values of our dataset (Team name, Current FIFA rank, Group, Previous Finals, Previous Titles, Previous Semifinals) by using the d3’s .html() method and setting the inner HTML to a given value. In our case, this is the information about the teams.

At this point, it is important to keep the user aware of the interactivity. You can achieve this by using a simple CSS rule. You can show that the element is clickable by changing the cursor from default to pointer:

This action is already an example of interactivity — by hovering the mouse over a particular element, a user triggers a change in the infographic.

However, as discussed above, the .on() method is the main method used for d3 interactivity. Here we need two parameters — the action that causes another function to be triggered. So with the help of this method, we basically just add or remove an event listener. The d3.js documentation page outlines the actions that are supported: https://github.com/d3/d3/blob/master/API.md.

Besides changing the cursor’s appearance, you can also highlight the element by changing the diameter of the surrounding circle — expand it on mouseover and shrink on mouseout:

For this action to be performed, a scope has to be given to our selector — d3.select(this). Now d3 is restricted from selecting only the first circle or selecting all circles (if we were to use d3.selectAll) on the whole canvas but remains within the boundaries of one <g> element with a class .team, containing the information about one team only. Now we can successfully expand and shrink the diameter of the surrounding circle with the corresponding commands on mouseover and mouseout events. For a smoother animation, we use attribute transition with a duration of 300 milliseconds.

Dynamically Rendering the Content

Statistics Table and Displaying the Winner Team on the Trophy Click

The .on(‘click’) method allows us to connect two separate parts of our infographic — the balls and the statistics table — and make them display meaningful information upon interaction with the user. As soon as the user clicks on one of the balls, the statistics table is filled with information about the selected team. Clicking on different balls changes the contents of the table.

We use a similar principle to display the name of the winner team of the 2018 World Cup. First, we select the path of a <g> with a class “outerCircle” which we manually added in the SVG panel and add a small transformation by changing the colour of the outer circle.

Upon the interaction — click — we are adding an event: selecting the element containing the name of the winner team, and changing its display from ‘none’ to ‘visible’. In this way, the user gets a signal that the element is interactive and upon an expected action — click — the result is presented.

Entry Animation

Now we come to the last bit of the interactivity and animation that we have added to our infographic. We are adding this part for fun and to demonstrate the potential of animated infographics with d3.js in the Repods platform.

In the function playAnimation(), we are chaining several d3 methods on the teamG element (the group of all the 9 balls).

The result — 9 circles are subsequently added and removed as the background of each ball image is creating a pulsing animation. We create this effect by inserting and removing a circle element to each ball and changing its diameter from 40 to 0. However, the most important parts here are the transition(), delay(), and duration() methods.

transition() has to be called to start and to end the animation in order to schedule a transition for the selected elements, in our case —the circles. With delay(), we specify the milliseconds after which the next method (in this example — the addition and removal of the “r” attribute) has to be called, so here we have 100 ms. Last, duration() sets the time limit for how long the animation has to be performed (800 ms).

This infographic displays the circles’ animation in two different ways: on the initial load, when the function playAnimation() is called, and by clicking on the infographic’s title. Keep in mind the method to be used while adding the circles. We have the option to use the .insert() and .append() methods. However, if we were to use .append(), the circles would be placed on top of the balls because the .append() method adds the element to the DOM last. By using the .insert() method, we are adding the circles exactly in the right position — before the balls in the DOM.

The World Cup 2018 might be over but the need for attractive, customizable, and interactive data visualizations is definitely here to stay. The combined forces of Repods and d3.js allow you to present your data in various static and dynamic infographics.

Do not hesitate — sign up to try the platform yourself at https://repods.io/ and follow our tutorial series Data Science with Repods on Medium.

--

--