Wednesday, 18 April 2012 at 7:25 pm

view the demo here
HTML source is at the bottom of the post

In part 1 and 2 of this series, I showed you how to graph a simple chart and how to use Chrome's developer tools to more effectively debug/test your code. In this part of the series, I will show you how to add interactivity to your data visualizations.

The key method in d3 used for adding interactivity is the '.on' method. I will go through a few examples of how to use the '.on' method and briefly go over some more advanced topics like event bubbling and using mouse coordinates.


Using the .on method

The '.on' method in d3.js is very simple to use. To bind a function to a 'mouseover' event, you just have to:

myD3Object.on('mouseover', function() {//your function code here});

Other mouse events you can use are:

  • mouseover - when your mouse is hovering over the object
  • mouseout - when your mouse leaves the object
  • mousedown - when your left mouse button is held down
  • mouseup - when you let go of your left mouse butotn
  • click - when you click your mouse button
  • mousemove - when your mouse moves around in the current object
Note that 'mousedown' and 'mouseup' events are part of the 'click' event. 
 


Scope binding

Great, now you can bind a function to a mouse event. But what can we do with that? Just like the '.style' or '.attr' functions discussed in previous blog entries, the annonymous function that you pass to the '.on' function has the data and index number of the currently selected object:

d3.selectAll('rect')
.data(myData)
.enter()
.append('rect')
.on('click', function(d,i) {
d3.select(this).style('fill','red');
});

In the above example, I've created a rectangle for every piece of data in the 'myData' array. Then with the '.on' method, I've passed an anonymous function where the argument 'd' is the element in the array and 'i' is the index of the element in the array. 

The scope that is bound to the anonymous function is the DOM node of the rectangle object. We can select the DOM node with d3 and then style the object as previously discussed. In this example, when the rectangle is clicked, it will change it's fill color to red.


Adding interactivity 

Using the example of the bar chart we've used previously, let's add a couple of mouse events. New code is towards the bottom of the code sample. Here is a link to a live demo.

<html>
<head>
<script type="text/javascript" src="d3.v2.js"></script>
<style>
.fig {
font-family:Arial;
font-size:10pt;
color:darkgray;
}
</style>
</head>
<body>
<script type="text/javascript">
de = [{'count': 728, 'name': 'sample0'}, {'count': 824, 'name': 'sample1'}, {'count': 963, 'name': 'sample2'}, {'count': 927, 'name': 'sample3'}, {'count': 221, 'name': 'sample4'}, {'count': 574, 'name': 'sample5'}, {'count': 733, 'name': 'sample6'}, {'count': 257, 'name': 'sample7'}, {'count': 879, 'name': 'sample8'}, {'count': 620, 'name': 'sample9'}];
var mySVG = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500)
.style('position','absolute')
.style('top',50)
.style('left',40)
.attr('class','fig');
var heightScale = d3.scale.linear()
.domain([0, d3.max(de,function(d) { return d.count;})])
.range([0, 400]);
mySVG.selectAll(".xLabel")
.data(de)
.enter().append("svg:text")
.attr("x", function(d,i) {return 113 + (i * 22);})
.attr("y", 435)
.attr("text-anchor", "middle")
.text(function(d,i) {return d.name;})
.attr('transform',function(d,i) {return 'rotate(-90,' + (113 + (i * 22)) + ',435)';});

mySVG.selectAll(".yLabel")
.data(heightScale.ticks(10))
.enter().append("svg:text")
.attr('x',80)
.attr('y',function(d) {return 400 - heightScale(d);})
.attr("text-anchor", "end")
.text(function(d) {return d;});

mySVG.selectAll(".yTicks")
.data(heightScale.ticks(10))
.enter().append("svg:line")
.attr('x1','90')
.attr('y1',function(d) {return 400 - heightScale(d);})
.attr('x2',320)
.attr('y2',function(d) {return 400 - heightScale(d);})
.style('stroke','lightgray');

var myBars = mySVG.selectAll('rect')
.data(de)
.enter()
.append('svg:rect')
.attr('width',20)
.attr('height',function(d,i) {return heightScale(d.count);})
.attr('x',function(d,i) {return (i * 22) + 100;})
.attr('y',function(d,i) {return 400 - heightScale(d.count);})
.style('fill','lightblue')
.on('mouseover', function(d,i) {
d3.select(this)
.style('fill','gray');
statusText
.text(d.count)
.attr('fill','white')
.attr("text-anchor", "start")
.attr("x", (i * 22) + 105)
.attr("y", 414)
.attr('transform', 'rotate(-90,' + (100 + (i * 22)) + ',400)'); })
.on('mouseout', function(d,i) {
statusText
.text('');
d3.select(this)
.style('fill','lightblue');
});
var statusText = mySVG.append('svg:text');
</script>
</body>
</html>

This added code will add mouse events to the bar chart so when the mouse hovers over the bars, they will turn gray and display the count. When the mouse leaves the bars, the bar will return to light blue and the count will disappear.


Mouse coordinates

To get the current mouse coordinates, you can use the d3.mouse object:

var coord = d3.mouse(document.body);
var xPos = coord[0];
var yPos = coord[1]; 

The coordinates are calculated relative to the object you pass into the d3.mouse object. In this case, it will get mouse coordinates relative to document.body, which is basically the entire page.


Event bubbling

Events are passed upwards (bubbling) as it goes through the DOM structure. For example, lets say we have a button (buttonA) within another button (buttonB). When buttonA is clicked, the 'click' event is registered on buttonA. But since buttonA is within buttonB, the 'click' event will bubble up to the parent element and register also on buttonB, firing the click functions of both buttonA and buttonB.

To stop event bubbling, you can use the d3.event object and the '.stopPropagation' method:

d3.event.stopPropagation();

You can also use the '.preventDefault()' method to prevent browser default actions from happening:

d3.event.stopDefault();


What's next

Interactivity is really what separates javascript figures from static figures. It provides a better visual representation of your data and allows the user to gain access to the entirety of the analysis. In the next few weeks, I'll post more entries on:








Search

Categories


Archive