This is Part II of our two-piece article Interactive Infographics with d3.js in Repods. In what follows, we are going to show you how to approach the task of adding interactivity to a complex infographic in the Repods data science platform.
The full code for this infographic can be found under this gist file.
Choosing the data
The first step towards adding interactivity is selecting 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:
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 < 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 towards adding interactivity 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 and has to be referred to performing selections within the code in this panel, e.g.
Adding the balls
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 ‘
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
.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.
Representing the teams
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”.
Besides, each ball is colored differently. To achieve this, we are creating a variable
teamColor() which internally uses 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.
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 element. The 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(), ,filter(), .map(), .split(), .splice() 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 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. You can do this 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 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. We do this 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
Final steps towards adding interactivity are finding ways to render the content dynamically.
Statistics table and displaying the winner team on the trophy click
.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 with a class “outerCircle” which we manually added in the SVG panel. The we add a small transformation by changing the colour of the outer circle.
Upon the interaction—click—we are adding an event. We select the element containing the name of the winner team, and change its display from ‘none’ to ‘visible’. In this way, the user gets a signal that the element is interactive. Upon an expected action—click—the user gets a display of the result.
Now we come to the last bit of adding interactivity and animation to our infographic. We are adding this part for fun. But we also 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() has to be called to start and to end the animation in order to schedule a transition for the selected elements. In our case, these are the circles. With delay(), we specify the milliseconds after which the next method has to be called. In this case, this is the addition and removal of the “r” attribute. 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
.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. Adding interactivity is easy and you should try it out yourself.
Do not hesitate—sign up to try the platform yourself at Repods.
Subscribe to our blog!
Stay up to date with the latest data science and IoT tips and news.