Treehouse

Six Degrees of Kevin Bacon Using MooTools 1.2

By on  

As you can probably tell, I try to mix some fun in with my MooTools madness but I also try to make my examples as practical as possible. Well...this may not be one of those times.

I love movies and useless movie trivia so naturally I'm enamored with the Six Degrees of Kevin Bacon game. If you're unfamiliar with the game, Wikipedia has a great description. To summarize, you need to string together actors/actresses that have appeared in movies together with the last person being Kevin Bacon.

To bring that to the web, I felt that drag and drop would be the best method. I wanted the user to drag an actor's image into a specific drop box and the string of actors would be presented in that fashion. After a lot of work, I made it happen.

The CSS

.bad			{ background:#fc8976; border-color:#ff3918; }
.clear			{ clear:both; }
#crumb			{ float:right; margin:0 100px 0 0; padding:10px; border-left:1px solid #ccc; background:#eee; }
#dragable-holder	{ margin:20px 0; }
.dragable		{ position:relative; cursor:move; }
#droppable-holder	{  }
.droppable		{ border:2px solid #ccc; width:100px; height:100px; float:left; margin:0 20px 0 0; }
.good			{ background:#76fca8; border-color:#26af5a; }
.hint			{ color:#eee; }
.hint-show		{ color:#000; }
.hint-movie		{ padding:0 0 0 30px; font-style:italic; list-style-type:none; color:#333; }
.no-move		{ cursor:default; }

There's not very much CSS involved -- most of it was simply formatting the position of the crumb. Note that I've added the "move" cursor to elements that I want the user to know that they may drag. In this case, the images.

The XHTML

<!-- flow -->
<ul id="crumb">
	<li class="hint" rel="kevin">Kevin Bacon</li>
	<li class="hint-movie">Mystic River</li>
	<li class="hint" rel="sean">Sean Penn</li>
	<li class="hint-movie">The Thin Red Line</li>
	<li class="hint" rel="jim">Jim Caviezel</li>
	<li class="hint-movie">The Count of Monte Cristo</li>
	<li class="hint" rel="dagmara">Dagmara Dominczyk</li>
	<li class="hint-movie">Running With Scissors</li>
	<li class="hint" rel="joseph">Joseph Fiennes</li>
	<li class="hint-movie">Killing Me Softly</li>
	<li class="hint" rel="heather">Heather Graham</li>
</ul>

<!-- holders -->
<div id="droppable-holder">
	<div id="holder1" class="droppable" rel="heather"></div>
	<div id="holder2" class="droppable" rel="joseph"></div>
	<div id="holder3" class="droppable" rel="dagmara"></div>
	<div id="holder4" class="droppable" rel="jim"></div>
	<div id="holder5" class="droppable" rel="sean"></div>
	<div id="holder6" class="droppable" rel="kevin"></div>
</div>
<div class="clear"></div>

<!-- images -->
<div id="dragable-holder">
	<img src="6degrees/heather.jpg" class="dragable tip" rel="heather" title="Heather Graham" />
	<img src="6degrees/sean.jpg" class="dragable tip" rel="sean" title="Sean Penn" />
	<img src="6degrees/jim.jpg" class="dragable tip" rel="jim" title="Jim Caviezel" />
	<img src="6degrees/joseph.jpg" class="dragable tip" rel="joseph" title="Joseph Fiennes" />
	<img src="6degrees/dagmara.jpg" class="dragable tip" rel="dagmara" title="Dagmara Dominczyk" />
	<img src="6degrees/kevin.jpg" class="dragable tip" rel="kevin" title="Kevin Bacon" />
</div><div class="clear"></div>

Not very much HTML here either. One simple list that servers as the crumb and the two containers which hold the dragables and the droppables. Also, note that the droppable rel attributes matches the same attribute on each dragable and crumb list item. This is very important.

The MooTools JavaScript

//when the dom is ready...
window.addEvent('domready',function() {
	
	document.ondragstart = function () { return false; }; //IE drag hack
	
	//for every dragable image...
	$$('.dragable').each(function(drag) {
		
		//this is where we'll save the initial spot of the image
		var position = drag.getPosition();
		
		//make it dragable, and set the destination divs
		new Drag.Move(drag, {
			//droppables: $$('.droppable'),
			droppables: '.droppable',
			onComplete: function() {
				if(drag.get('rel') == 'x')
				{
					drag.addClass('no-move');
					this.detach();
				}
			},
			onEnter: function(el,droppable) {
				//colors!
				droppable.addClass(el.get('rel') == droppable.get('rel') ? 'good' : 'bad');
			},
			onLeave: function(el,droppable) {
				droppable.removeClass('bad');
				if(droppable.get('rel') != 'x')
				{
					droppable.removeClass('good')
				}
			},
			onDrop: function(el, droppable) {
					
				if(droppable)
				{
					//if good...
					if(el.get('rel') == droppable.get('rel'))
					{
						//add the good class
						droppable.addClass('good');
						
						//show right side crumb item
						$$("#crumb li[rel='" + el.get('rel') + "']").addClass('hint-show');
						
						//set rel as flag later
						droppable.set('rel','x');
						
						//reposition
						el.inject(droppable);
						el.setStyles({'left':0,'top':0,'position':'relative','margin':0}); //hack -- wtf?
						
						//no mo!
						el.set('rel','x');
					}
					//bad drop, go back!
					else
					{
						droppable.removeClass('bad');
						el.setStyles({'left':0,'top':0,'position':'relative','margin-right':'20px'}); //hack -- wtf?
						el.inject($('dragable-holder'));
					}
				}
				else
				{
					  el.inject($('dragable-holder'));
				}
			 }
		});
		
		//position fix
		drag.setStyles({
			'top':0,
			'left':0,
			'margin-right':'20px'
		});
		
	});
});

The JavaScript was the difficult part of this project. I'll do my best to summarize:

  • For every element with the dragable class (the images), we use MooTools to make the element dragable.
  • We add a onComplete option to check to see if the image is already where it should be -- if so, disable any more dragging of this image.
  • For every droppable (or each container), we add a bunch of events:
    • Over: The over event is simple -- we simply add an event to the droppable that changes the background and border colors to indicate whether the user is hovering over the correct box or not.
    • Leave: We remove the bad class in all cases, but only remove the good if aimage isn't already in the correct spot.
    • Drop: If it's a good drop, we add the good class, show the crumb item, inject the image into the droppable, and lock the image in there (remove the drag). If it's a bad drop, we place the image back into the list of images.

Again, this isn't the most practical use of JavaScript drag/drop but it sure is fun!

Special Note

I can't mention this game without also mentioning that Kevin's created a charity called Six Degrees that benefits your favorite charity. If you get a moment, please check out the website.

ydkjs-6.png

Recent Features

  • An Interview with Eric&nbsp;Meyer

    Your early CSS books were instrumental in pushing my love for front end technologies. What was it about CSS that you fell in love with and drove you to write about it? At first blush, it was the simplicity of it as compared to the table-and-spacer...

  • Create a CSS&nbsp;Cube

    CSS cubes really showcase what CSS has become over the years, evolving from simple color and dimension directives to a language capable of creating deep, creative visuals.  Add animation and you've got something really neat.  Unfortunately each CSS cube tutorial I've read is a bit...

Incredible Demos

  • Create Your Own Dijit CSS Theme with LESS&nbsp;CSS

    The Dojo Toolkit seems to just get better and better.  One of the new additions in Dojo 1.6 was the use of LESS CSS to create Dijit themes.  The move to using LESS is a brilliant one because it makes creating your own Dijit theme...

  • Telephone Link&nbsp;Protocol

    We've always been able to create links with protocols other than the usual HTTP, like mailto, skype, irc ,and more;  they're an excellent convenience to visitors.  With mobile phone browsers having become infinitely more usable, we can now extend that convenience to phone numbers: The tel...

Discussion

  1. crush

    great work!
    but how can i fix the bug, that the dragables flow over the screen, when they injected back in the dropable-holder?
    it is possible to get some ‘order’ in that area?

  2. crush

    ok, at the end i found out how u can avoid that bad behaviour:
    u have to use

    drag.setStyles({‘left’:0,’top’:0,’position’:’relative’,’margin’:0}); //hack — wtf?

    everytime – also when u want to inject a dragable back to the droppable-area!
    otherwise your dragables suck over the whole screen ;-)

    again thx for the nice work! i added a XMLHttpRequest to valide each move and it works fine!

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Use Code Editor