The Pinball Effect
Got something to say?
Share your comments on this topic with other web professionals
In: Articles
Published on June 12, 2006
“Links should be big enough to hit with a dirty, sticky mouse.”
Andrew Morrall
“…one of the most ignored principles in design…”
Bruce Tognazzini
Yadda × 3: Preamble
It’s quicker and easier to use a bigger link than a smaller one—this is obvious. There’s even a mathematical formula, Fitts’ Law, to calculate just how much quicker. Presented below is a simple way to transform an entire area of a Web page into a link from within it, effectively expanding the link’s size, using Cascading Style Sheets, JavaScript and the Document Object Model. I call this the Pinball Effect because the cursor lights up various areas of the screen sort of like a pinball does zipping around the playfield.
The Pinball Effect is ideal for items in detailed lists, such as entries on a blog’s index pages (see the example). It degrades gracefully if the client’s scripting is off. And it adds no inline JavaScript to mess up your pristine HTML. I call the affected area the Pinball Scoop, the link within it the Pinball Sinkhole. And rest easy, this flip metaphor plunges no further.
Yalla × 2: Get On With It
First we’ll add the scoops and sinkholes to the page. Simply add pinball-scoop
to the class definition of each of the areas that should light up, and pinball-sinkhole
to the class definition of the desired link within each area.
Snippet from pinball-effect.html
<div class="blog-teaser pinball-scoop"> <h2><a class="a-class pinball-sinkhole" href="http://crockford.com/javascript/survey.html">A Survey of the JavaScript Programming Language</a> by Douglas Crockford</h2> <p><a href="http://java.sun.com/pr/1995/12/pr951204-03.html">JavaScript</A> was developed by Brendan Eich at Netscape as the in-page scripting language for Navigator 2. It is a remarkably expressive dynamic programming language. Because of its linkage to Web browsers, it instantly became massively popular. It never got a trial period in which it could be corrected and polished based on actual use. The language is powerful and flawed.</p> </div> <div class="blog-teaser pinball-scoop"> <h2><a class="some-other-class-perhaps pinball-sinkhole" href="http://www.bigempire.com/filthy/lifeaquatic.html">Filthy Critic: The Life Aquatic</a></h2> <p>Director Anderson sure as shit loves making movies and cramming them with details, contraptions and fantastic visuals. He even loves dialog and writes bits that other writers would kill to have. It's just that he seems to be getting so Goddamned disinterested in people that he can't give them any sort of emotional trip that makes the physical one worth following. He did the same thing with <a href="http://www.bigempire.com/filthy/royaltenenbaums.html">Royal Tenenbaums</a>, where he spent so much time sketching the characters' quirks that he never bothered letting them express anything.</p> </div>
That’s it, that’s the only inline addition to the HTML, so if scripting is off then nothing happens; degradation is graceful.
Second, what allows us to set mouseover
and onclick
behavior without inline JavaScript is the JavaScript file containing the pinballEffect
function and its subfunctions (with thanks to Peter-Paul Koch’s Quirksmode Mouseovers tutorial). The function traverses the document, teasing out any elements with the pinball-scoop
class and giving them the subfunctions treatment whenever triggered by mouse behavior.
Here’s the code:
pinball-effect.js
var W3CDOM = (document.createElement && document.getElementsByTagName); window.onload = pinballEffect; function pinballEffect() { if (!W3CDOM) return; var allElements = document.getElementsByTagName('*'); var originalBackgrounds=new Array(); for (var i=0; i<allElements.length; i++) { if (allElements[i].className.indexOf('pinball-scoop') !=-1) { allElements[i].onmouseover = mouseGoesOver; allElements[i].onmouseout = mouseGoesOut; allElements[i].onclick = mouseGoesClick; } } } function mouseGoesOver() { originalClassNameString = this.className; this.className += " pinball-on"; this.style.cursor = "pointer"; this.style.cursor = "hand"; } function mouseGoesOut() { this.className = originalClassNameString; } function mouseGoesClick() { var allThisAreasElements = this.getElementsByTagName('*'); for (var j=0; j<allThisAreasElements.length; j++) { if (allThisAreasElements[j].className.indexOf('pinball-sinkhole') != -1) { var url = allThisAreasElements[j].href; window.location = url; } } } pinballEffect();
In the pinballEffect
function, line 1 stops us from even trying if the browser can’t handle it. (Have I mentioned degradation? Grace?) The mouseGoesClick
function is similar to its parent pinballEffect
function, but rather than traversing the document seeking Pinball Scoops, it traverses a particular scoop seeking its sinkhole.
If there’s no click and the mouse goes out, the mouseGoesOut
function restores the scoop element’s class name string to whatever it was before the mouseGoesOver
function appended the pinball-on
class to it. This allows our elements to have any number of other classes, which is nice.
Note that the regular expression in mouseGoesClick
requires that that the sinkhole URL be within double quotes. And if you accidentally put more than one sinkhole link within a single scoop it’ll grab whichever comes first. I suspect.
The mouseover
cursor style can also be set in the CSS, but the browser seems to react more quickly from the JavaScript. To cover any browser disparities, I added it to both. And in case you didn’t know, Internet Explorer requires a hand rather than a pointer.
Save the code as a file and call it just above your </body>
tag:
<script type="text/javascript" src="pinball-effect.js"></script>
Now of course you need some CSS to actually show the difference between off and on states. The following is what I used for the example:
pinball-effect.css
body { color: #000; } .blog-teaser { width: 27em; padding: 0px; background-color: #aaa; margin-bottom: 1ex; } .pinball-on { background-color: yellow; } h2, p { padding: 10px; margin: 0px; }
Again, save it and call it from somewhere in the header:
<link rel="stylesheet" type="text/css" media="screen" href="pinball-effect.css" />
Play with Others
There’s another, simpler technique to enlarge the clickable area around a link using CSS only. The Denton Method is designed for inline links, so it complements the Pinball Effect, which is more appropriate for titles. Just incorporate this into your stylesheets:
p a:hover { background-color: #you-choose; position:relative; z-index: 1; padding: .4em 0 .55em 0; }
Speaking of padding, I will now round out this article with yet another person’s work. In More Nifty Corners, Alessandro Fulciniti ingeniously uses no graphics—CSS and JavaScript only—to bestow upon any and all elements rounded corners. (He has recently released a newer and more powerful version, Nifty Corners Cube, but it does not yet play pinball well.)
How do you think he does it? I don’t know, but just add a call to a copy of his More Nifty Corners CSS to your header:
<link rel="stylesheet" type="text/css" media="screen" href="nifty-corners.css" />
And add a call to a copy of the More Nifty Corners JavaScript file to that great thriving JavaScript reef just above your </body>
tag:
<script type="text/javascript" src="nifty-corners.js"></script> <script> window.onload=function(){ if(!NiftyCheck()) return; Rounded("div.pinball-area","all","white","transparent","smooth");} </script>
If you want to use the newer Nifty Corners Cube, it’ll round your corners before the mouseover
, but not during it. It does work just fine with mouseover
behavior other than changing the background color, such as changing the text color. To upgrade to Cube, remove the Nifty CSS—the new code calls its own styling autonomously—and move the Pinball and Cube JavaScript calls from just above the </body>
tag to the document’s header, placing the Cube call last.
Insert Coin
OK, now that you understand what I’m talking about, just download the pinball-effect.zip file containing all the required files:
- pinball-effect.html
- pinball-effect.js
- pinball-effect.css
- nifty-corners.js
- nifty-corners.css
- denton.css
Better Next Time
The script could be improved by checking that the sinkhole URL is correctly formed, only activating onmouseover
and onclick
behavior if it is; after all, we don’t want to perpetuate and even exaggerate errors—but for now we will.
Techniques Abused Above
- Quirksmode Mouseovers by Peter-Paul Koch
- More Nifty Corners by Alessandro Fulciniti (and check out the newer but, alas, incompatible Nifty Corners Cube)
- Fitts’ Law and Text Links by David Benton
Links of interest
- Fitts’ Law at Virginia Tech, including demo (Java)
- Fitts’ Law, one of the First Principles of Interaction Design by Bruce Tognazzini
- Pinball terminology
- A rather different Pinball Effect by James Burke
Got something to say?
Share your comments with other professionals (4 comments)
Related Topics: CSS, DOM, Scripting, Web Design
Adam Khan is a Web developer and Nokia 6630 fan. He is currently building Planadu.