Home > Tutorials > D3 Tutorials > Hover notes
Interactive Notes
While you might know the theory of clicking and hovering and general interaction, you might not have any good ideas about how to use that just yet.
Let’s fix that.
Adding in notes
When we last left our visualization, it was just a bunch of circles that changed color when you moused over them.
Open example in new window
<svg></svg>
<script>
var datapoints = [
{ 'title' : 'Pride and Prejudice' , 'author' : 'Jane Austen' , 'words' : 120000 , 'published' : 1813 },
{ 'title' : 'Cryptonomicon' , 'author' : 'Neal Stephenson' , 'words' : 415000 , 'published' : 1999 },
{ 'title' : 'Great Gatsby' , 'author' : 'F. Scott Fitzgerald' , 'words' : 47094 , 'published' : 1925 },
{ 'title' : 'Song of Solomon' , 'author' : 'Toni Morrison' , 'words' : 92400 , 'published' : 1977 },
{ 'title' : 'White Teeth' , 'author' : 'Zadie Smith' , 'words' : 169000 , 'published' : 2000 }
];
var height = 200 ,
width = 400 ;
var svg = d3 . select ( "svg" ). attr ( 'height' , height ). attr ( 'width' , width );
var max_words = d3 . max ( datapoints , function ( d ) { return d [ 'words' ]; });
var x_scale = d3 . scale . linear (). domain ([ 1800 , 2015 ]). range ([ 0 , width ]);
var y_scale = d3 . scale . linear (). domain ([ 0 , 500000 ]). range ([ height , 0 ]);
svg . selectAll ( 'circle' )
. data ( datapoints )
. enter ()
. append ( 'circle' )
. attr ( 'r' , 10 )
. attr ( 'cx' , function ( d ) { return x_scale ( d [ 'published' ]) })
. attr ( 'cy' , function ( d ) { return y_scale ( d [ 'words' ]) })
. on ( 'mouseover' , function ( d , i ) {
// make the mouseover'd element
// bigger and red
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 20 )
. attr ( 'fill' , '#ff0000' );
})
. on ( 'mouseout' , function ( d , i ) {
// return the mouseover'd element
// to being smaller and black
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 10 )
. attr ( 'fill' , '#000000' );
})
</script>
Open example in new window
But what is each datapoint? How many pages is it? Who wrote it?
One way to solve this problem is by having an area wholly dedicated to information. Let’s throw in a div that will contain all of our info. While we could in theory position it anywhere, let’s just keep it up top at first.
<div>
<h3> Book Title</h3>
<h4> Book Author</h4>
<p> Published in: <strong> year</strong></p>
<p> Word count: <strong> published</strong></p>
</div>
And in action:
Open example in new window
<div>
<h3> Book Title</h3>
<h4> Book Author</h4>
<p> Published in: <strong> year</strong></p>
<p> Word count: <strong> published</strong></p>
</div>
<svg></svg>
<script>
var datapoints = [
{ 'title' : 'Pride and Prejudice' , 'author' : 'Jane Austen' , 'words' : 120000 , 'published' : 1813 },
{ 'title' : 'Cryptonomicon' , 'author' : 'Neal Stephenson' , 'words' : 415000 , 'published' : 1999 },
{ 'title' : 'Great Gatsby' , 'author' : 'F. Scott Fitzgerald' , 'words' : 47094 , 'published' : 1925 },
{ 'title' : 'Song of Solomon' , 'author' : 'Toni Morrison' , 'words' : 92400 , 'published' : 1977 },
{ 'title' : 'White Teeth' , 'author' : 'Zadie Smith' , 'words' : 169000 , 'published' : 2000 }
];
var height = 200 ,
width = 400 ;
var svg = d3 . select ( "svg" ). attr ( 'height' , height ). attr ( 'width' , width );
var max_words = d3 . max ( datapoints , function ( d ) { return d [ 'words' ]; });
var x_scale = d3 . scale . linear (). domain ([ 1800 , 2015 ]). range ([ 0 , width ]);
var y_scale = d3 . scale . linear (). domain ([ 0 , 500000 ]). range ([ height , 0 ]);
svg . selectAll ( 'circle' )
. data ( datapoints )
. enter ()
. append ( 'circle' )
. attr ( 'r' , 10 )
. attr ( 'cx' , function ( d ) { return x_scale ( d [ 'published' ]) })
. attr ( 'cy' , function ( d ) { return y_scale ( d [ 'words' ]) })
. on ( 'mouseover' , function ( d , i ) {
// make the mouseover'd element
// bigger and red
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 20 )
. attr ( 'fill' , '#ff0000' );
})
. on ( 'mouseout' , function ( d , i ) {
// return the mouseover'd element
// to being smaller and black
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 10 )
. attr ( 'fill' , '#000000' );
})
</script>
Open example in new window
In order to change what’s in our infobox, we’ll need to update its text. And in order to update the text, we need a way to select the text. class
and/or id
to the rescue!
Let’s overdo it a little:
<div class= "infobox" >
<h3 class= "title" > Book Title</h3>
<h4 class= "author" > Book Author</h4>
<p> Published in: <strong class= "published" > year</strong></p>
<p> Word count: <strong class= "words" > published</strong></p>
</div>
Inside of our on('mouseover')
, we are currently using this
to change the size and color of the circle. We also have available to us d
, the datapoint. You’ll need to keep this clear, so let’s repeat to ourselves:
d
is the data point, and this
is its representation on the page.
d
is the data point, and this
is its representation on the page.
d
is the data point, and this
is its representation on the page.
d
is a simple key-value dictionary, so in order to get the title, we use d['title']
. Author comes out of d['author']
, etc. Those will be the values we put into the infobox.
Now that we know how to get the values from our data point, we just need to grab the elements from the infobox! d3.select
will work fine here.
. on ( 'mouseover' , function ( d , i ) {
// Select the title, use .text to set the content
// d is the data point
d3 . select ( ".infobox .title" ). text ( d [ 'title' ]);
// Leave the highlight part alone
// d3.select(this) is the circle on the page
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 20 )
. attr ( 'fill' , '#ff0000' );
})
Now we just need to expand it to cover all of the data we’re looking for - the book’s title, author, publication year, and word count.
Open example in new window
<div class= "infobox" >
<h3 class= "title" > Book Title</h3>
<h4 class= "author" > Book Author</h4>
<p> Published in: <strong class= "published" > year</strong></p>
<p> Word count: <strong class= "words" > published</strong></p>
</div>
<svg></svg>
<script>
var datapoints = [
{ 'title' : 'Pride and Prejudice' , 'author' : 'Jane Austen' , 'words' : 120000 , 'published' : 1813 },
{ 'title' : 'Cryptonomicon' , 'author' : 'Neal Stephenson' , 'words' : 415000 , 'published' : 1999 },
{ 'title' : 'Great Gatsby' , 'author' : 'F. Scott Fitzgerald' , 'words' : 47094 , 'published' : 1925 },
{ 'title' : 'Song of Solomon' , 'author' : 'Toni Morrison' , 'words' : 92400 , 'published' : 1977 },
{ 'title' : 'White Teeth' , 'author' : 'Zadie Smith' , 'words' : 169000 , 'published' : 2000 }
];
var height = 200 ,
width = 400 ;
var svg = d3 . select ( "svg" ). attr ( 'height' , height ). attr ( 'width' , width );
var max_words = d3 . max ( datapoints , function ( d ) { return d [ 'words' ]; });
var x_scale = d3 . scale . linear (). domain ([ 1800 , 2015 ]). range ([ 0 , width ]);
var y_scale = d3 . scale . linear (). domain ([ 0 , 500000 ]). range ([ height , 0 ]);
svg . selectAll ( 'circle' )
. data ( datapoints )
. enter ()
. append ( 'circle' )
. attr ( 'r' , 10 )
. attr ( 'cx' , function ( d ) { return x_scale ( d [ 'published' ]) })
. attr ( 'cy' , function ( d ) { return y_scale ( d [ 'words' ]) })
. on ( 'mouseover' , function ( d , i ) {
// Select the element by class, use .text to set the content
d3 . select ( ".infobox .title" ). text ( d [ 'title' ]);
d3 . select ( ".infobox .author" ). text ( d [ 'author' ]);
d3 . select ( ".infobox .published" ). text ( d [ 'published' ]);
d3 . select ( ".infobox .words" ). text ( d [ 'words' ]);
// make the mouseover'd element
// bigger and red
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 20 )
. attr ( 'fill' , '#ff0000' );
})
. on ( 'mouseout' , function ( d , i ) {
// return the mouseover'd element
// to being smaller and black
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 10 )
. attr ( 'fill' , '#000000' );
})
</script>
Open example in new window
Looking good! Now our only problem is that our notes are always saying something, even when we aren’t hovering.
We have two CSS-based choices to hide it, one is display: none;
and the other one is visibility: hidden;
.
display: none
will completely remove it from the page , and the space it was taking up will have other stuff move into it (namely, the svg). Then when it comes back it’ll push the svg down.
visibility: hidden
will make it seem invisible . The notes will still take up the space, the svg will still be a little bit below, and then when the notes come back the svg won’t jump around.
In this case we like visibility: hidden;
. In order to make it work we need to do three things:
Hide the info box as soon as the page loads
Show the info box when mouseover
happens
Hide the info box when mouseout
happens
Hide by default:
Let’s manually set the style
of the infobox to be invisbile. I know I’ve said a thousand times to never write styles like this, but… just this once!
<div class= "infobox" style= "visibility: hidden;" >
<h3 class= "title" > Book Title</h3>
<h4 class= "author" > Book Author</h4>
<p> Published in: <strong class= "published" > year</strong></p>
<p> Word count: <strong class= "words" > published</strong></p>
</div>
Show during mouseover:
Change the visiblity
to be visible
when the mouse goes over an element
d3 . select ( ".infobox" ). style ( 'visibility' , 'visible' );
Hide after mouseout:
Change visibility
to be hidden
when it goes off
d3 . select ( ".infobox" ). style ( 'visibility' , 'hidden' );
What have we got now?
Open example in new window
<div class= "infobox" style= "visibility: hidden;" >
<h3 class= "title" > Book Title</h3>
<h4 class= "author" > Book Author</h4>
<p> Published in: <strong class= "published" > year</strong></p>
<p> Word count: <strong class= "words" > published</strong></p>
</div>
<svg></svg>
<script>
var datapoints = [
{ 'title' : 'Pride and Prejudice' , 'author' : 'Jane Austen' , 'words' : 120000 , 'published' : 1813 },
{ 'title' : 'Cryptonomicon' , 'author' : 'Neal Stephenson' , 'words' : 415000 , 'published' : 1999 },
{ 'title' : 'Great Gatsby' , 'author' : 'F. Scott Fitzgerald' , 'words' : 47094 , 'published' : 1925 },
{ 'title' : 'Song of Solomon' , 'author' : 'Toni Morrison' , 'words' : 92400 , 'published' : 1977 },
{ 'title' : 'White Teeth' , 'author' : 'Zadie Smith' , 'words' : 169000 , 'published' : 2000 }
];
var height = 200 ,
width = 400 ;
var svg = d3 . select ( "svg" ). attr ( 'height' , height ). attr ( 'width' , width );
var max_words = d3 . max ( datapoints , function ( d ) { return d [ 'words' ]; });
var x_scale = d3 . scale . linear (). domain ([ 1800 , 2015 ]). range ([ 0 , width ]);
var y_scale = d3 . scale . linear (). domain ([ 0 , 500000 ]). range ([ height , 0 ]);
svg . selectAll ( 'circle' )
. data ( datapoints )
. enter ()
. append ( 'circle' )
. attr ( 'r' , 10 )
. attr ( 'cx' , function ( d ) { return x_scale ( d [ 'published' ]) })
. attr ( 'cy' , function ( d ) { return y_scale ( d [ 'words' ]) })
. on ( 'mouseover' , function ( d , i ) {
// Select the element by class, use .text to set the content
d3 . select ( ".infobox .title" ). text ( d [ 'title' ]);
d3 . select ( ".infobox .author" ). text ( d [ 'author' ]);
d3 . select ( ".infobox .published" ). text ( d [ 'published' ]);
d3 . select ( ".infobox .words" ). text ( d [ 'words' ]);
// Show the infobox
d3 . select ( ".infobox" ). style ( 'visibility' , 'visible' );
// make the mouseover'd element
// bigger and red
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 20 )
. attr ( 'fill' , '#ff0000' );
})
. on ( 'mouseout' , function ( d , i ) {
// Hide the infobox
d3 . select ( ".infobox" ). style ( 'visibility' , 'hidden' );
// return the mouseover'd element
// to being smaller and black
d3 . select ( this )
. transition ()
. duration ( 100 )
. attr ( 'r' , 10 )
. attr ( 'fill' , '#000000' );
})
</script>
Open example in new window
Tada!