Tag Archiv: learn

Making a Custom YouTube Video Player With YouTube’s APIs

Video presentations are a great addition to any product page. With a presentation you can showcase your product’s features without making the visitor read through long paragraphs of text. But apart from producing the video, you still need to manually convert it and find (or code) some sort of flash player that will display it on your site.

The other possible path is that you upload it to a video sharing site such as youtube, but you are going to have a rough time trying to incorporate the player into your design.

Luckily for us, YouTube does provide a solution to this problem – their chromeless player (a stripped down version of the regular embeddable player), which allow you to build and style your own custom controls. This way you have both a quick and secure way to include videos in your pages, and the freedom to customize any way you might want to.

The Idea

Today we are going to make a jQuery plugin which uses YouTube’s chromeless player, and creates our own set of minimalistic controls, which allows for perfect integration with your designs. The supported controls include a Play/Pause/Replay button, and a clickable progress bar.

The plugin is going to use YouTube’s gdata api to determine whether embedding has been allowed for the video, and fetch extensive information about it, such as title, description, tags, screenshots & more, which you can use to improve the plugin.

Using the plugin to embed videos is extremely easy:

// Embed a video into the #player div:
$('#player').youTubeEmbed('http://www.youtube.com/watch?v=u1zgFlCw8Aw');

// Chaining is also supported:
$('#player').youTubeEmbed('http://www.youtube.com/watch?v=u1zgFlCw8Aw');
		.youTubeEmbed('http://www.youtube.com/watch?v=AsdfFdwlzdAw');

You can also specify a width for the embedded video (the height will be calculated automatically depending on the aspect ratio), and choose to disable the progress bar:

$('#player').youTubeEmbed({
	video			: 'http://www.youtube.com/watch?v=u1zgFlCw8Aw',
	width			: 600, 		// Height is calculated automatically
	progressBar	: false		// Hide the progress bar
});

You can grab the plugin from the download button above, and start with the first step.

Step 1 – XHTML

Our plugin depends on jQuery SWFObject to embed the SWF files in the page. Below you can see the combined markup that is generated by both of the plugins.

youtube-player.html

<div class="flashContainer" style="width: 640px; height: 360px;">

	<object height="360" width="640" id="video_26ELpS3Wc4Q" type="application/x-shockwave-flash"
	data="http://www.youtube.com/apiplayer?enablejsapi=1&version=3">

		<param value="always" name="allowScriptAccess">
		<param value="transparent" name="wmode">
		<param value="video_id=26ELpS3Wc4Q&playerapiid=26ELpS3Wc4Q"
		name="flashvars">
		<param value="http://www.youtube.com/apiplayer?enablejsapi=1&version=3"
		name="movie">

	</object>

	<div class="controlDiv play"></div>

	<div class="progressBar">
		<div class="elapsed"></div>
	</div>
</div>

The .flashContainerDiv is dynamically created by the plugin for each video on the page. It is populated with the embed code generated by SWFObject, the .controlDiv (which acts as a play/pause button) and the progress bar.

As mentioned above, the insertion of the player itself is handled by the SWFObject plugin. Depending on the browser, it can output either an object element, or a non-standard embed element for IE. This lifts the burden from us and allows us to concentrate on tasks such as querying YouTube’s APIs and building the player controls.

Custom YouTube Player

Custom YouTube Player

Step 2 – jQuery

The plugin’s code is located in the youTubeEmbed-jquery-1.0.js file. However, before being able to use it, you need to include the latest version of the jQuery library in the page, along with the jQuery SWFObject plugin and lastly script.js, which inserts two videos in the demonstration page and handles the submissions of the preview form.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="jquery.swfobject.1-1-1.min.js"></script>
<script src="youTubeEmbed/youTubeEmbed-jquery-1.0.js"></script>
<script src="script.js"></script>

Before we start digging into the player plugin’s code, lets take a look at a sample response from YouTube’s gdata api. It can give you a lot of useful information about a video, including duration, access control (both of which used by the plugin) and all sorts of additional data such as title, description, tags, screenshots and more.

Sample JSON response

{
    "id": "u1zgFlCw8Aw",
    "uploaded": "2008-03-05T01:22:17.000Z",
    "updated": "2010-07-23T01:02:42.000Z",
    "uploader": "GoogleDevelopers",
    "category": "People",
    "title": "The YouTube API: Upload, Player APIs and more!",
    "description": "Listen to the YouTube APIs and Tools team talk about...",
    "tags": ["youtube", "launch", "api", "engineering"],
    "thumbnail": {
        "sqDefault": "http://i.ytimg.com/vi/u1zgFlCw8Aw/default.jpg",
        "hqDefault": "http://i.ytimg.com/vi/u1zgFlCw8Aw/hqdefault.jpg"
    },
    "player": {
        "default": "http://www.youtube.com/watch?v=u1zgFlCw8Aw",
        "mobile": "http://m.youtube.com/details?v=u1zgFlCw8Aw"
    },
    "content": {
        "1": "rtsp://v4.cache5.c.youtube.com/CiILE..",
        "5": "http://www.youtube.com/v/u1zgFlCw8Aw?f..",
        "6": "rtsp://v3.cache4.c.youtube.com/CiILENy73.."
    },
    "duration": 259,
    "location": "san bruno, ca",
    "rating": 4.3,
    "likeCount": "119",
    "ratingCount": 144,
    "viewCount": 251024,
    "favoriteCount": 164,
    "commentCount": 118,
    "accessControl": {
        "syndicate": "allowed",
        "commentVote": "allowed",
        "rate": "allowed",
        "list": "allowed",
        "comment": "allowed",
        "embed": "allowed",
        "videoRespond": "allowed"
    }
}

All the fields of this response objects are available as properties in the data variable (data.fieldname). You could potentially modify the plugin to show the title with a link to the video page on youtube, or show the rating of the video.

Now lets dive directly into the script’s source code.

youTubeEmbed-jquery-1.0.js – Part 1

(function($){

	$.fn.youTubeEmbed = function(settings){

		// Settings can be either a URL string,
		// or an object

		if(typeof settings == 'string'){
			settings = {'video' : settings}
		}

		// Default values

		var def = {
			width		: 640,
			progressBar	: true
		};

		settings = $.extend(def,settings);

		var elements = {
			originalDIV	: this,	// The "this" of the plugin
			container	: null,	// A container div, inserted by the plugin
			control		: null,	// The control play/pause button
			player		: null,	// The flash player
			progress	: null,	// Progress bar
			elapsed		: null	// The light blue elapsed bar
		};

		try{	

			settings.videoID = settings.video.match(/v=(\w+)/)[1];

			// safeID is a stripped version of videoID,
			// ready for use as a JavaScript function name

			settings.safeID = settings.videoID.replace(/[^a-z0-9]/ig,'');

		} catch (e){
			// If the url was invalid, just return the "this"
			return elements.originalDIV;
		}

		// Fetch data about the video from YouTube's API

		var youtubeAPI = 'http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc';

		$.get(youtubeAPI,{'q':settings.videoID},function(response){

			var data = response.data;

			if(!data.totalItems || data.items[0].accessControl.embed!="allowed"){

				// If the video was not found, or embedding is not allowed;

				return elements.originalDIV;
			}

			// data holds API info about the video:

			data = data.items[0];

			settings.ratio = 3/4;
			if(data.aspectRatio == "widescreen"){
				settings.ratio = 9/16;
			}

			settings.height = Math.round(settings.width*settings.ratio);

We start by defining our script as a jQuery plugin by adding it as a function to the $.fn object. To make the code easier to follow and read, I put all the elements of the page, such as the control and the progressBar divs in a structure called elements.

After extracting the id of the video (a unique 11 character sequence after the ?v= parameter), we send a JSONP request to youtube’s gdata API. Depending on whether such a video exists, and on whether embedding is allowed on it, we proceed with calculating the aspect ratio. The height of the video is calculated by using this ratio and multiplying it to the width.

youTubeEmbed-jquery-1.0.js – Part 2

			// Creating a container inside the original div, which will
			// hold the object/embed code of the video

			elements.container = $('<div>',{className:'flashContainer',css:{
				width	: settings.width,
				height	: settings.height
			}}).appendTo(elements.originalDIV);

			// Embedding the YouTube chromeless player
			// and loading the video inside it:

			elements.container.flash({
				swf			: 'http://www.youtube.com/apiplayer?enablejsapi=1&version=3',
				id			: 'video_'+settings.safeID,
				height		: settings.height,
				width		: settings.width,
				allowScriptAccess:'always',
				wmode		: 'transparent',
				flashvars	: {
					"video_id"		: settings.videoID,
					"playerapiid"	: settings.safeID
				}
			});

			// We use get, because we need the DOM element
			// itself, and not a jquery object:

			elements.player = elements.container.flash().get(0);

			// Creating the control Div. It will act as a ply/pause button

			elements.control = $('<div>',{className:'controlDiv play'})
							   .appendTo(elements.container);

			// If the user wants to show the progress bar:

			if(settings.progressBar){
				elements.progress =	$('<div>',{className:'progressBar'})
									.appendTo(elements.container);

				elements.elapsed =	$('<div>',{className:'elapsed'})
									.appendTo(elements.progress);

				elements.progress.click(function(e){

					// When a click occurs on the progress bar, seek to the
					// appropriate moment of the video.

					var ratio = (e.pageX-elements.progress.offset().left)/elements.progress.outerWidth();

					elements.elapsed.width(ratio*100+'%');
					elements.player.seekTo(Math.round(data.duration*ratio), true);
					return false;
				});

			}

In the second part of the code, we use the SWFObject plugin to generate the embed code of  the youtube chromeless player. Notice that the id of the video is passed as a flashvar so the player can load it directly. The safeID variable (a JavaScript safe version of the videoid) becomes the value of the id parameter of the to-be generated object element. This way we can later fetch the DOM element by running document.getElementById(‘video_’+settings.safeID) and get access to the methods which control the youtube player (play, pause etc).

youTubeEmbed-jquery-1.0.js – Part 3

var initialized = false;

// Creating a global event listening function for the video
// (required by YouTube's player API):

window['eventListener_'+settings.safeID] = function(status){

	var interval;

	if(status==-1)	// video is loaded
	{
		if(!initialized)
		{
			// Listen for a click on the control button:

			elements.control.click(function(){
				if(!elements.container.hasClass('playing')){

					// If the video is not currently playing, start it:

					elements.control.removeClass('play replay').addClass('pause');
					elements.container.addClass('playing');
					elements.player.playVideo();

					if(settings.progressBar){
						interval = window.setInterval(function(){
							elements.elapsed.width(
					((elements.player.getCurrentTime()/data.duration)*100)+'%'
							);
						},1000);
					}

				} else {

					// If the video is currently playing, pause it:

					elements.control.removeClass('pause').addClass('play');
					elements.container.removeClass('playing');
					elements.player.pauseVideo();

					if(settings.progressBar){
						window.clearInterval(interval);
					}
				}
			});

			initialized = true;
		}
		else{
			// This will happen if the user has clicked on the
			// YouTube logo and has been redirected to youtube.com

			if(elements.container.hasClass('playing'))
			{
				elements.control.click();
			}
		}
	}

In order to be able to control the video player, we need to be notified when certain events (like playback stopped, video ready etc) occur. Normally, this would mean that we need to pass a callback function, which is executed by the player every time such an event happens.

Unfortunately, flash can only execute functions if they are defined in the global scope and cannot see the functions which are defined inside the plugin. However, by creating functions with unique names (with the safeID) and explicitly adding them to the window object we can make this happen. If it weren’t for this little trick, it would be impossible for the plugin to work.

youTubeEmbed-jquery-1.0.js – Part 4

				if(status==0){ // video has ended
					elements.control.removeClass('pause').addClass('replay');
					elements.container.removeClass('playing');
				}
			}

			// This global function is called when the player is loaded.
			// It is shared by all the videos on the page:

			if(!window.onYouTubePlayerReady)
			{
				window.onYouTubePlayerReady = function(playerID){
					document.getElementById('video_'+playerID).addEventListener('onStateChange','eventListener_'+playerID);
				}
			}
		},'jsonp');

		return elements.originalDIV;
	}

})(jQuery);

The event listening function we created in the previous section of the code, is attached to the player with the addEventListener method. It is called every time when a “stateChange” occurs (playback start, playback pause, end of playback etc). A numeric code is passed to the event listening function as a parameter, corresponding to the event.

Now lets take a look at how our plugin is used.

script.js

$(document).ready(function(){

	$('#player').youTubeEmbed('http://www.youtube.com/watch?v=u1zgFlCw8Aw');

	/*
		//You can alternatively pass an object:

		$('#player').youTubeEmbed({
			video			: 'http://www.youtube.com/watch?v=u1zgFlCw8Aw',
			width			: 600, 		// Height is calculated automatically
			progressBar	: false		// Hide the progress bar
		});

	*/

});

You just need to call the youTubeEmbed() method and pass it either a string or a configuration object. If a string is passed, it is assumed to be the URL of a YouTube video. If you are passing an object make sure that you are passing the video property with a correct video URL.

Video Playing with a Progress Bar

Video Playing with a Progress Bar

Step 3 – CSS

Finally we are left with applying a few CSS styles to the player. They will change the design of the player controls and define the way they are positioned in the player window.

youTubeEmbed-jquery-1.0.css

.flashContainer{

	/*	Setting the container to relative positioning
		so we can center the control div */

	position:relative;
	overflow:hidden;
}

.progressBar{
	display:none;
	position:absolute;
	width:auto;
	height:8px;
	left:20px;
	right:105px;
	bottom:20px;
	background-color:#141414;
	overflow:hidden;
	cursor:pointer;

	/* A light CSS3 bottom highlight */

	-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.3);
	-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.3);
	box-shadow:0 1px 0 rgba(255, 255, 255, 0.3);
}

.progressBar .elapsed{
	position:absolute;
	width:0;
	height:100%;
	background-color:#1fa2f6;
	border-right:1px solid #49AFF0;
}

.controlDiv{
	/* Centering the control div */
	position:absolute;
	width:120px;
	height:120px;
	cursor:pointer;
	top:50%;
	left:50%;
	margin:-60px 0 0 -60px;
}

.controlDiv.play{
	background:url('img/play.png') no-repeat center center;
}

.controlDiv.replay{
	background:url('img/replay.png') no-repeat center center;
}

.controlDiv.pause{
	background:url('img/pause.png') no-repeat -99999px;
}

.flashContainer:hover .controlDiv.pause{
	background-position:center center;
}

/* Only show the progress bar when the video is playing */

.flashContainer.playing:hover .progressBar{
	display:block;
}

To customize the look of the player you just need to change the color values above. Also you can edit the png files with the play/pause buttons. Clearly this is much easier than modifying the looks of the default youtube player. It also strips all of the unnecessary chrome and leaves you with what matters most – your video.

With this our Custom YouTube Player plugin is complete!

Did you like this tutorial? Share your thoughts in the comment section below.

Colortip – a jQuery Tooltip Plugin

In this tutorial we are going to write a simple jQuery tooltip plugin. It is going to convert the title attributes of elements withing your page, into a series of colorful tooltips. Six color themes are available, so you can easily match it with the rest of your design.

Step 1 – XHTML

The plugin we are about to write today, works by converting the title of an element on the page to a structure of three spans, which form a tooltip, displayed on hover. So if you start with a regular link like this one:

<a href="http://tutorialzine.com/" class="blue" title="Go to Tutorialzine">Tutorialzine</a>

jQuery will convert it to the markup you can see below.

<a class="blue colorTipContainer" href="http://tutorialzine.com/">Tutorialzine
<span class="colorTip" style="margin-left: -60px;">Go to Tutorialzine
<span class="pointyTipShadow"></span>
<span class="pointyTip"></span>
</span>
</a>

Notice that the first block of code above specifies a “blue” class name. This is because we are overriding the default color of the tip (yellow). You can choose from red, blue, green, yellow, white and black, or you can create your own colors in the plugin stylesheet.

Users which have JavaScript disabled are still going to see the regular title tooltips on the page, so the script degrades gracefully.

Step 2 – CSS

To display the colorful tips on your page, you first need to include the plugin stylesheet file in the head section of the html document.

<link rel="stylesheet" type="text/css" href="colortip-1.0/colortip-1.0-jquery.css"/>

You could also just copy the styles from the file and paste them directly in your main stylesheet, if you do not want to keep a separate include file. This stylesheet defines both the positioning and the design of the tooltips. It comes with six color themes, and you can easily add more.

colortip-1.0-jquery.css – Part 1

.colorTipContainer{
	position:relative;
	text-decoration:none !important;
}

.colorTip{
	/* This class is assigned to the color tip span by jQuery */

	display:none;
	position:absolute;
	left:50%;
	top:-30px;
	padding:6px;

	background-color:white;
	font-family:Arial,Helvetica,sans-serif;
	font-size:11px;
	font-style:normal;
	line-height:1;
	text-decoration:none;
	text-align:center;
	text-shadow:0 0 1px white;
	white-space:nowrap;

	-moz-border-radius:4px;
	-webkit-border-radius:4px;
	border-radius:4px;
}

.pointyTip,.pointyTipShadow{
	/* Setting a thick transparent border on a 0x0 div to create a triangle */
	border:6px solid transparent;
	bottom:-12px;
	height:0;
	left:50%;
	margin-left:-6px;
	position:absolute;
	width:0;
}

.pointyTipShadow{
	/* The shadow tip is 1px larger, so it acts as a border to the tip */
	border-width:7px;
	bottom:-14px;
	margin-left:-7px;
}

The .colorTipContainer class is assigned to the elements to which the color tips are added. It sets their positioning to relative, so that the tips can be centered above them.

A neat CSS trick is used to create the triangular pointy tips. As  you know, divs and spans can only take a rectangular shape (or an ellipse / rounded rectangle if you apply border-radius). However, if you apply a thick border to a 0 by 0 element, the only way the borders are going to fit is to take up a triangular space on each side. Then, by setting the default border color to transparent, you can make visible any of the four triangular shapes.

Making a triangular shape with a div

Making a triangular shape with a div

This is actually done on both the .pointyTip and the .pointyTipShadow spans, in order to give an impression that the pointy tip has a border applied so it matches the rest of the colortip design. Now lets take a closer look at the six color themes.

colortip-1.0-jquery.css – Part 2

/* 6 Available Color Themes */

.white .pointyTip{ border-top-color:white;}
.white .pointyTipShadow{ border-top-color:#ddd;}
.white .colorTip{
	background-color:white;
	border:1px solid #DDDDDD;
	color:#555555;
}

.yellow .pointyTip{ border-top-color:#f9f2ba;}
.yellow .pointyTipShadow{ border-top-color:#e9d315;}
.yellow .colorTip{
	background-color:#f9f2ba;
	border:1px solid #e9d315;
	color:#5b5316;
}

.blue .pointyTip{ border-top-color:#d9f1fb;}
.blue .pointyTipShadow{ border-top-color:#7fcdee;}
.blue .colorTip{
	background-color:#d9f1fb;
	border:1px solid #7fcdee;
	color:#1b475a;
}

.green .pointyTip{ border-top-color:#f2fdf1;}
.green .pointyTipShadow{ border-top-color:#b6e184;}
.green .colorTip{
	background-color:#f2fdf1;
	border:1px solid #b6e184;
	color:#558221;
}

.red .pointyTip{ border-top-color:#bb3b1d;}
.red .pointyTipShadow{ border-top-color:#8f2a0f;}
.red .colorTip{
	background-color:#bb3b1d;
	border:1px solid #8f2a0f;
	color:#fcfcfc;
	text-shadow:none;
}

.black .pointyTip{ border-top-color:#333;}
.black .pointyTipShadow{ border-top-color:#111;}
.black .colorTip{
	background-color:#333;
	border:1px solid #111;
	color:#fcfcfc;
	text-shadow:none;
}

Remember that only the borders are the only visible part of the pointy tips. You can add your own color themes by following the same structure.

Step 3 – jQuery

First you need to include the jquery library and the plugin files to the page, after which our script.js file, which is going to utilize the plugin and add colortips to the links on the page.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="colortip-1.0/colortip-1.0-jquery.js"></script>
<script type="text/javascript" src="script.js"></script>

For performance reasons, I’ve put this code at the bottom of the colortips.html file. This allow for the page design to be rendered before the browser blocks for the loading of the js files.

Now lets take a look at the colortip the plugin.

colortip-1.0-jquery.js – Part 1

(function($){
	$.fn.colorTip = function(settings){

		var defaultSettings = {
			color		: 'yellow',
			timeout		: 500
		}

		var supportedColors = ['red','green','blue','white','yellow','black'];

		/* Combining the default settings object with the supplied one */
		settings = $.extend(defaultSettings,settings);

		/*
		*	Looping through all the elements and returning them afterwards.
		*	This will add chainability to the plugin.
		*/

		return this.each(function(){

			var elem = $(this);

			// If the title attribute is empty, continue with the next element
			if(!elem.attr('title')) return true;

			// Creating new eventScheduler and Tip objects for this element.
			// (See the class definition at the bottom).

			var scheduleEvent = new eventScheduler();
			var tip = new Tip(elem.attr('title'));

			// Adding the tooltip markup to the element and
			// applying a special class:

			elem.append(tip.generate()).addClass('colorTipContainer');

			// Checking to see whether a supported color has been
			// set as a classname on the element.

			var hasClass = false;
			for(var i=0;i<supportedColors.length;i++)
			{
				if(elem.hasClass(supportedColors[i])){
					hasClass = true;
					break;
				}
			}

			// If it has been set, it will override the default color

			if(!hasClass){
				elem.addClass(settings.color);
			}

When you call the plugin, you can pass a configuration object, which holds the default color and the timeout after which the tooltips disappear on mouseleave.

You can choose from six possible colors and assign them as a class name to the element. The plugin would check if a color class is present, and if it is not, it will apply the default one you’ve passed in the config object. If you’ve not passed a config object, yellow will be used instead.

Colortip jQuery Plugin

Colortip jQuery Plugin

colortip-1.0-jquery.js – Part 2

			// On mouseenter, show the tip, on mouseleave set the
			// tip to be hidden in half a second.

			elem.hover(function(){

				tip.show();

				// If the user moves away and hovers over the tip again,
				// clear the previously set event:

				scheduleEvent.clear();

			},function(){

				// Schedule event actualy sets a timeout (as you can
				// see from the class definition below).

				scheduleEvent.set(function(){
					tip.hide();
				},settings.timeout);

			});

			// Removing the title attribute, so the regular OS titles are
			// not shown along with the tooltips.

			elem.removeAttr('title');
		});

	}

	/*
	/	Event Scheduler Class Definition
	*/

	function eventScheduler(){}

	eventScheduler.prototype = {
		set	: function (func,timeout){

			// The set method takes a function and a time period (ms) as
			// parameters, and sets a timeout

			this.timer = setTimeout(func,timeout);
		},
		clear: function(){

			// The clear method clears the timeout

			clearTimeout(this.timer);
		}
	}

In the second part of the plugin code, we bind event listeners to the hover event (combination of  a mouseenter and a mouseleave event).

Notice the event scheduler class. It is used to set a function to be executed after a predetermined period of time. At its heart lays a call to setTimeout. A clear() method is also provided as a part of the class, so previously scheduled events can be destroyed (useful when you move your mouse away from a tooltip and then move it back over before it has disappeared).

colortip-1.0-jquery.js – Part 3

	/*
	/	Tip Class Definition
	*/

	function Tip(txt){
		this.content = txt;
		this.shown = false;
	}

	Tip.prototype = {
		generate: function(){

			// The generate() method returns either a previously generated element
			// stored in the tip variable, or generates and saves it in tip for
			// later use, after which returns it.

			return this.tip || (this.tip = $('<span class="colorTip">'+this.content+
											 '<span class="pointyTipShadow"></span><span class="pointyTip"></span></span>'));
		},
		show: function(){
			if(this.shown) return;

			// Center the tip and start a fadeIn animation
			this.tip.css('margin-left',-this.tip.outerWidth()/2).fadeIn('fast');
			this.shown = true;
		},
		hide: function(){
			this.tip.fadeOut();
			this.shown = false;
		}
	}

})(jQuery);

In the last part of the code we define the tip class. It has a generate, show and hide methods. An object from this class is created for each of the tooltips. The generate method is called first, and the tooltip is stored in the local this.tip variable. The show and hide methods operate on this variable to run fade in/out animations.

Now we are only left with calling the plugin and adding colortips to all the links on the page.

script.js

$(document).ready(function(){

	/* Adding a colortip to any tag with a title attribute: */

	$('[title]').colorTip({color:'yellow'});

});

The configuration object here might as well be omitted, because yellow is the default value anyway. But you could specify a different color for the tooltips. If you apply red, green, blue, white, yellow or black as a class name of the element, the design of the tooltip will be overridden.

With this our Colortip plugin is complete!

Wrapping it up

Using the plugin we wrote today is really easy, as it does not require any specific configuration. It just takes a series of elements and replaces their title attributes with colorful tooltips. You can also easily create your own tooltip designs by just including three additional style classes to your stylesheet and adding an element to the supported colors array in colortip-1.0-jquery.js.

What do you think? How would you improve this plugin?

Making a Slick MobileApp Website with jQuery & CSS

As a web developer, you have to bring creativity and attention to detail to your work. It is often the little things that leave a lasting impression to a visitor. Be it a lovable character illustration, or unique slideshow, it is usually not the information you present that is memorable, but the way you present it.

Today we are making a complete jQuery & CSS website for a fictional mobile application. It is going to feature semantic markup and a progressively enhanced slideshow effect. It will allow the user to see four of the most popular smartphones running the mobile app.

Step 1 – XHTML

When building websites it is important that you lay your code in a semantic way. This would include using tags for what they were supposed to be used for. You should use headings for titles, paragraphs for text (instead of generic divs) and lists where applicable.

mobileapp.html

<div id="page">

    <h1 id="logoh1"><a href="/" id="logo">MobileApp - the most useful mobile app!</a></h1>

    <div id="phoneCarousel">
    	<div class="previous arrow"></div>
        <div class="next arrow"></div>

        <div id="stage">
            <img id="iphone" class="default" src="img/phones/iphone.png" width="270" height="400" alt="iPhone" />
            <img id="nexus" src="img/phones/nexus_one.png" width="270" height="400" alt="Nexus One" />
            <img id="nokia" src="img/phones/nokia.png" width="270" height="400" alt="Nokia" />
            <img id="blackberry" src="img/phones/blackberry.png" width="270" height="400" alt="BlackBerry" />
        </div>
    </div>

    <img class="availableAppStore" src="img/available_on_the_appstore.png" width="230" height="80" alt="Available on the Appstore" />

    <div class="text">
    	<h3><img src="img/thumb.png" alt="MobileApp" width="114" height="114" class="thumb" />A wonderful app</h3>
        <p>Lorem ipsum dolor sit amet.. </p>
    </div>

    <div class="text">
    	<h3><img src="img/thumb.png" alt="MobileApp" width="114" height="114" class="thumb" />More awesome facts</h3>
        <p>Lorem ipsum dolor sit amet.. </p>
    </div>

</div>

This is all the markup that is used to display the website. The h1 heading holds a hyperlink which is styled as the logo (the logo image is set as the background of the hyperlink and a negative text indent is used to hide the text of the link).

After this we have the #phoneCarousel div and inside it are the arrows and the stage. The phone images inside the stage are rotated with jQuery as you will see in a moment.

Lastly we have the Available on the Appstore badge, and two blocks of text.

Slick MobileApp Website - Slideshow

Slick MobileApp Website - Slideshow

Step 2 – CSS

CSS is responsible for converting our semantic markup into a true website. Take a closer look at the #stage styles in the second part of the code, as these are what make the animation possible.

styles.css – Part 1

body{
	font-size:14px;
	color:#515151;
	background:url('img/bg.png') repeat-x #f6f8f9;
	font-family:'Myriad Pro',Arial, Helvetica, sans-serif;
}

#logoh1{ margin:40px 0 0;}

#page{
	/* This the main container div */
	width:1000px;
	min-height:700px;
	margin:0 auto;
	background:url('img/bokeh.jpg') no-repeat 0 120px;
	position:relative;
	padding-top:1px;
}

#phoneCarousel{
	/*	This is the carousel section, it
		contains the stage and the arrows */
	height:390px;
	margin:90px auto 120px;
	position:relative;
	width:800px;
}

#phoneCarousel .arrow{
	/* The two arrows */
	width:44px;
	height:44px;
	background:url('img/arrows.png') no-repeat;
	position:absolute;
	top:50%;
	margin-top:-22px;
	left:0;
	cursor:pointer;
}

#phoneCarousel .next{
	/* Individual styles for the next icon */
	background-position:right top;
	left:auto;
	right:0;
}

/* Hover styles */

#phoneCarousel .arrow:hover{
	background-position:left bottom;
}

#phoneCarousel .next:hover{
	background-position:right bottom;
}

After defining the  body styles we can move on to styling the #page div, which holds everything together. Its background image is offset 120px vertically, so it matches the background of the body, filling the full width of the page.

Next is the #phoneCarousel div. It has a relative positioning applied, so the stage (where all the animations take place) can be properly centered. The previous/next arrows are styled as well.

styles.css – Part 2

#logo{
	background:url('img/logo.png') no-repeat;
	height:40px;
	text-indent:-9999px;
	width:210px;
	display:block;
}

#stage{
	/* The stage contains the animated phone images */
	left:50%;
	margin-left:-350px;
	position:absolute;
	width:700px;
	height:100%;
}

#stage img{
	/* Hiding all the images by default */
	display:none;
}

#stage .default{
	/*	This class is applied only to the iphone img by default
		and it is the only one visible if JS is disabled */
	display:block;
	left:50%;
	margin-left:-135px;
	position:absolute;
}

#stage .animationReady{
	/* This class is assigned to the images on load */
	display:block;
	position:absolute;
	top:0;
	left:0;
}

.text{	margin-top:70px;width:700px;}

.text p,
.text h3{
	padding-bottom:15px;
	line-height:1.4;
	text-align:justify;
}

.text h3{	font-size:30px;}
.text p{	font-size:20px;}

.thumb{	float:left;margin-right:40px;}

.availableAppStore{float:right;}

In the second part of the stylesheet, we continue with the #stage styles. The phone pictures are hidden by default, so if JavaScript is disabled, the user is not left with a bunch of scattered images.

As you will see in the next step, the animation is achieved through changing the top and left CSS properties. For this to work, the images must be absolutely positioned. This is why the .animatonReady class is assigned on load with jQuery (if JS is disabled, this style would not be applied).

Lastly we style the text blocks, which explain details about our fictional MobileApp.

Text Blocks

Text Blocks

Step 3 – jQuery

When you click on one of the arrows, an animation starts, which uses sine and cosine calculations to move and scale down the images, creating the illusion of a circular movement. It is not as complicated as it sounds, as you can see for yourself from the code below.

script.js

$(document).ready(function(){

	var deg=0;

	/* Storing all the images into a variable */

	var images	= $('#stage img').removeClass('default').addClass('animationReady');
	var dim		= { width:images.width(),height:images.height()};

	var cnt = images.length;

	/* Finding the centers of the animation container: */
	var centerX = $('#stage').width()/2;
	var centerY = $('#stage').height()/2 - dim.height/2;

	function rotate(step,total){
		// This function will loop through all the phone images, and rotate them
		// with "step" degrees (10 in this implementation) till total > 0

		/* Increment the degrees: */
		deg+=step;

		var eSin,eCos,newWidth,newHeight,q;

		/* Loop through all the images: */
		for(var i=0;i<cnt;i++){

			/* Calculate the sine and cosine for the i-th image */

			q = ((360/cnt)*i+deg)*Math.PI/180;
			eSin		= Math.sin(q);
			eCos		= Math.cos(q);

			/*
			/	With the sine value, we can calculate the vertical movement,
			/	and the cosine will give us the horizontal movement.
			*/

			q = (0.6+eSin*0.4);
			newWidth	= q*dim.width;
			newHeight	= q*dim.height;

			/*
			/	We are using the calculated sine value (which is in the range
			/	of -1 to 1) to calculate the opacity and z-index. The
 			/	frontmost image has a sine value of 1, while the backmost
			/	one has a sine value of -1.
			*/

			// eq() extracts the image at the i-th position:

			images.eq(i).css({
				top			: centerY+15*eSin,
				left		: centerX+200*eCos,
				opacity		: 0.8+eSin*0.2,
				marginLeft	: -newWidth/2,
				zIndex		: Math.round(80+eSin*20)
			}).width(newWidth).height(newHeight);
		}

		total-=Math.abs(step);
		if(total<=0) return false;

		// Setting the function to be run again in 40 milliseconds (equals to 25 frames per second):
		setTimeout(function(){rotate(step,total)},40);

	}

	// Running the animation once at load time (and moving the iPhone into view):
	rotate(10,360/cnt);

	$('#phoneCarousel .previous').click(function(){
		// 360/cnt lets us distribute the phones evenly in a circle
		rotate(-10,360/cnt);
	});

	$('#phoneCarousel .next').click(function(){
		rotate(10,360/cnt);
	});
});

To start an animation you just need to call the rotate function with two arguments – a step, and a total rotation, both of which are numbers. Step can be negative, which would mean that the rotation is run in the opposite way. Each time the function is run, total is decremented with the absolute value of the step, and once it reaches zero the animation is stopped.

In a number of places in this code, you can see that I’ve used a specific calculation – 360/cnt. This is done to distribute the phones evenly (360 being the number of degrees in a circle).  This way you can add or remove images and they will be properly animated.

Slick MobileApp Website

Slick MobileApp Website

With this our Slick MobleApp Website is complete!

Wrapping it up

Today we made a complete jQuery & CSS website for a fictional mobile application. You are free to modify the code and use it any way you see fit. If you liked the tutorial be sure to subscribe to our RSS feed, follow us on twitter, or leave a comment in the section below.

Simple AJAX Commenting System

This time, we are making a Simple AJAX Commenting System. It will feature a gravatar integration and demonstrate how to achieve effective communication between jQuery and PHP/MySQL with the help of JSON.

Step 1 – XHTML

First, lets take a look at the markup of the comments. This code is generated by PHP in the Comment class, which we are going to take a look at in a moment.

demo.php

<div class="comment">
    <div class="avatar">
        <a href="http://tutorialzine.com/">
        <img src="http://www.gravatar.com/avatar/112fdf7a8fe3609e7af2cd3873b5c6bd?size=50&default=http%3A%2F%2Fdemo.tutorialzine.com%2F2010%2F06%2Fsimple-ajax-commenting-system%2Fimg%2Fdefault_avatar.gif">
        </a>
    </div>

    <div class="name"><a href="http://tutorialzine.com/">Person's Name</a></div>
    <div title="Added at 06:40 on 30 Jun 2010" class="date">30 Jun 2010</div>
    <p>Comment Body</p>
</div>

The avatar div contains a hyperlink (if the user entered a valid URL when submitting the comment) and an avatar image, which is fetched from gravatar.com. We will return to this in the PHP step of the tut. Lastly we have the name and time divs, and the comment body.

The other important element in the XHTML part is the comment form. It is sent via POST. All fields except for the URL field are required.

demo.php

<div id="addCommentContainer">
	<p>Add a Comment</p>
	<form id="addCommentForm" method="post" action="">
    	<div>
        	<label for="name">Your Name</label>
        	<input type="text" name="name" id="name" />

            <label for="email">Your Email</label>
            <input type="text" name="email" id="email" />

            <label for="url">Website (not required)</label>
            <input type="text" name="url" id="url" />

            <label for="body">Comment Body</label>
            <textarea name="body" id="body" cols="20" rows="5"></textarea>

            <input type="submit" id="submit" value="Submit" />
        </div>
    </form>
</div>

The form is submitted via AJAX. The validation is performed entirely in the backend by submit.php, as you will see in the jQuery step of the tutorial. Every field has a corresponding label element, with an appropriate for attribute.

Simple AJAX Commenting System

Simple AJAX Commenting System

Step 2 – PHP

PHP handles the communication with the MySQL database and generates the markup of the comments. It is also on the receiving end of the AJAX requests and inserts the comment data to the comments table. You can see the code that prints the comments to the page below.

demo.php

/*
/	Select all the comments and populate the $comments array with objects
*/

$comments = array();
$result = mysql_query("SELECT * FROM comments ORDER BY id ASC");

while($row = mysql_fetch_assoc($result))
{
	$comments[] = new Comment($row);
}

The MySQL query selects all the entries from the database and fills the $comments array with objects of the comment class, which you will see below. This array is outputted later in the execution of the script.

demo.php

/*
/	Output the comments one by one:
*/

foreach($comments as $c){
	echo $c->markup();
}

Each comment has a markup() method, which generates valid HTML code ready to be printed to the page. You can see the definition of this method and the class below.

The class takes a row from the database (fetched with mysql_fetch_assoc() ) and stores it in the private variable $data. It is available only to the methods of the class and cannot be accessed from outside.

comment.class.php – Part 1

class Comment
{
	private $data = array();

	public function __construct($row)
	{
		/*
		/	The constructor
		*/

		$this->data = $row;
	}

	public function markup()
	{
		/*
		/	This method outputs the XHTML markup of the comment
		*/

		// Setting up an alias, so we don't have to write $this->data every time:
		$d = &$this->data;

		$link_open = '';
		$link_close = '';

		if($d['url']){

			// If the person has entered a URL when adding a comment,
			// define opening and closing hyperlink tags

			$link_open = '<a href="'.$d['url'].'">';
			$link_close =  '</a>';
		}

		// Converting the time to a UNIX timestamp:
		$d['dt'] = strtotime($d['dt']);

		// Needed for the default gravatar image:
		$url = 'http://'.dirname($_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]).
				'/img/default_avatar.gif';

		return '

			<div class="comment">
				<div class="avatar">
					'.$link_open.'
					<img src="http://www.gravatar.com/avatar/'.
				md5($d['email']).'?size=50&default='.
				urlencode($url).'" />
					'.$link_close.'
				</div>

				<div class="name">'.$link_open.$d['name'].$link_close.'</div>
				<div class="date" title="Added at '.
				date('H:i \o\n d M Y',$d['dt']).'">'.
				date('d M Y',$d['dt']).'</div>
				<p>'.$d['body'].'</p>
			</div>
		';
	}

This script uses gravatar to present avatars in the comments. For those of you, who have not used gravatar, this is a really useful service, which lets you associate an avatar with your email address. The avatar image can easily be fetched by passing an md5() encoded hash of your email address to gravatar.com. This is exactly what we do on line 48.

Notice line 39 above it – the script tries to figure out the URL at which it is located, and determines the exact address of the default_avatar.gif image. This gif is passed to gravatar along the md5 hash, so if no avatar was found for this particular email, the fallback image is displayed instead.

comment.class.php – Part 2

	public static function validate(&$arr)
	{
		/*
		/	This method is used to validate the data sent via AJAX.
		/
		/	It return true/false depending on whether the data is valid, and populates
		/	the $arr array passed as a paremter (notice the ampersand above) with
		/	either the valid input data, or the error messages.
		*/

		$errors = array();
		$data	= array();

		// Using the filter_input function introduced in PHP 5.2.0

		if(!($data['email'] = filter_input(INPUT_POST,'email',FILTER_VALIDATE_EMAIL)))
		{
			$errors['email'] = 'Please enter a valid Email.';
		}

		if(!($data['url'] = filter_input(INPUT_POST,'url',FILTER_VALIDATE_URL)))
		{
			// If the URL field was not populated with a valid URL,
			// act as if no URL was entered at all:

			$url = '';
		}

		// Using the filter with a custom callback function:

		if(!($data['body'] = filter_input(INPUT_POST,'body',FILTER_CALLBACK,
						array('options'=>'Comment::validate_text'))))
		{
			$errors['body'] = 'Please enter a comment body.';
		}

		if(!($data['name'] = filter_input(INPUT_POST,'name',FILTER_CALLBACK,
						array('options'=>'Comment::validate_text'))))
		{
			$errors['name'] = 'Please enter a name.';
		}

		if(!empty($errors)){

			// If there are errors, copy the $errors array to $arr:

			$arr = $errors;
			return false;
		}

		// If the data is valid, sanitize all the data and copy it to $arr:

		foreach($data as $k=>$v){
			$arr[$k] = mysql_real_escape_string($v);
		}

		// Ensure that the email is in lower case (for a correct gravatar hash):
		$arr['email'] = strtolower(trim($arr['email']));

		return true;

	}

The validate() method above (also part of the class) is defined as static. This means that it can be evoked directly like Comment::validate(), without the need of creating an object of the class. What this method does, is validate the input data which is submitted via AJAX.

This method uses the new filter functions, which are available as of PHP 5.2.0. These allow us to easily validate and filter any input data that is passed to the script. For example filter_input(INPUT_POST,’url’,FILTER_VALIDATE_URL) means that we are checking whether $_POST['url'] is a valid URL address. If it is, the function returns the value of the variable, otherwise it return false.

This is really useful, as up until now, we had to use custom regular expressions to validate data (and have series of if statements). Also, another advantage is that this data is fetched before any configuration-specific transformations (like magic quotes) are applied.

We also have the option of specifying a custom function which is going to apply some more advanced modifications of the data, as you can see from lines 31 and 37.

comment.class.php – Part 3

	private static function validate_text($str)
	{
		/*
		/	This method is used internally as a FILTER_CALLBACK
		*/

		if(mb_strlen($str,'utf8')<1)
			return false;

		// Encode all html special characters (<, >, ", & .. etc) and convert
		// the new line characters to <br> tags:

		$str = nl2br(htmlspecialchars($str));

		// Remove the new line characters that are left
		$str = str_replace(array(chr(10),chr(13)),'',$str);

		return $str;
	}

}

The last method is validate_text, which we are passing as a callback function in the two filter_input calls above. It encodes all special HTML characters, effectively blocking XSS attacks. It also replaces the newline characters with <br /> line breaks.

submit.php

/*
/	This array is going to be populated with either
/	the data that was sent to the script, or the
/	error messages:
/*/

$arr = array();

$validates = Comment::validate($arr);

if($validates)
{
	/* Everything is OK, insert to database: */

	mysql_query("	INSERT INTO comments(name,url,email,body)
					VALUES (
						'".$arr['name']."',
						'".$arr['url']."',
						'".$arr['email']."',
						'".$arr['body']."'
					)");

	$arr['dt'] = date('r',time());
	$arr['id'] = mysql_insert_id();

	/*
	/	The data in $arr is escaped for the mysql insert query,
	/	but we need the unescaped text, so we apply,
	/	stripslashes to all the elements in the array:
	/*/

	$arr = array_map('stripslashes',$arr);

	$insertedComment = new Comment($arr);

	/* Outputting the markup of the just-inserted comment: */

	echo json_encode(array('status'=>1,'html'=>$insertedComment->markup()));

}
else
{
	/* Outputting the error messages */
	echo '{"status":0,"errors":'.json_encode($arr).'}';
}

submit.php receives the comment form data via an AJAX request. It validates it and outputs a JSON object with either the XHTML markup of the successfully inserted comment, or a list of error messages. jQuery uses the status property to determine whether to display the error messages or add the comment markup to the page.

You can see two example responses below.

Successful response

{
    "status": 1,
    "html": "Html Code Of The Comment Comes Here..."
}

The html property contains the code of the comment, similar to markup in step one.

Failure response

{
    "status": 0,
    "errors": {
        "email": "Please enter a valid Email.",
        "body": "Please enter a comment body.",
        "name": "Please enter a name."
    }
}

On failure, jQuery loops through the errors object, and outputs the errors next to the fields that caused them.

Fancy CSS3 & jQuery Submit Form

Fancy CSS3 & jQuery Submit Form

Step 3 – CSS

Now that we have all the markup properly generated and displayed on the page, we can move on to styling it.

styles.css – Part 1

.comment,
#addCommentContainer{

	/* Syling the comments and the comment form container */

	padding:12px;
	width:400px;
	position:relative;
	background-color:#fcfcfc;
	border:1px solid white;
	color:#888;
	margin-bottom:25px;

	/* CSS3 rounded corners and drop shadows */

	-moz-border-radius:10px;
	-webkit-border-radius:10px;
	border-radius:10px;

	-moz-box-shadow:2px 2px 0 #c2c2c2;
	-webkit-box-shadow:2px 2px 0 #c2c2c2;
	box-shadow:2px 2px 0 #c2c2c2;
}

.comment .avatar{

	/*
	/	The avatar is positioned absolutely,
	/	and offset outside the comment div
	/*/

	height:50px;
	left:-70px;
	position:absolute;
	width:50px;
	background:url('img/default_avatar.gif') no-repeat #fcfcfc;

	/* Centering it vertically: */

	margin-top:-25px;
	top:50%;

	-moz-box-shadow:1px 1px 0 #c2c2c2;
	-webkit-box-shadow:1px 1px 0 #c2c2c2;
	box-shadow:1px 1px 0 #c2c2c2;
}

The .comment divs and the #addCommentContainer are styled at once because they share most of the styling. A number of CSS3 rules are applied, including rounded corners and a box-shadow. Needless to say, these do not work in older browsers, but as they are purely presentational, the script will still work without them.

styles.css – Part 2

.comment .avatar img{
	display:block;
}

.comment .name{
	font-size:20px;
	padding-bottom:10px;
	color:#ccc;
}

.comment .date{
	font-size:10px;
	padding:6px 0;
	position:absolute;
	right:15px;
	top:10px;
	color:#bbb;
}

.comment p,
#addCommentContainer p{
	font-size:18px;
	line-height:1.5;
}

#addCommentContainer input[type=text],
#addCommentContainer textarea{

	/* Styling the inputs */

	display:block;
	border:1px solid #ccc;
	margin:5px 0 5px;
	padding:3px;
	font-size:12px;
	color:#555;
	font-family:Arial, Helvetica, sans-serif;
}

#addCommentContainer textarea{
	width:300px;
}

label{
	font-size:10px;
}

label span.error{
	color:red;
	position:relative;
	right:-10px;
}

#submit{

	/* The submit button */

	background-color:#58B9EB;
	border:1px solid #40A2D4;
	color:#FFFFFF;
	cursor:pointer;
	font-family:'Myriad Pro',Arial,Helvetica,sans-serif;
	font-size:14px;
	font-weight:bold;
	padding:4px;
	margin-top:5px;

	-moz-border-radius:4px;
	-webkit-border-radius:4px;
	border-radius:4px;
}

#submit:hover{
	background-color:#80cdf5;
	border-color:#52b1e2;
}

In the second part of the stylesheet, we style the comment and form elements. Notice the input[type=text] selector, which selects elements depending on the type attribute.

Step 4 – jQuery

Now lets continue with jQuery, which is the last step of this tutorial. After including the library to the bottom of the page (best for the perceived performance of the page) we can start coding the script file.

script.js

$(document).ready(function(){
	/* The following code is executed once the DOM is loaded */

	/* This flag will prevent multiple comment submits: */
	var working = false;

	/* Listening for the submit event of the form: */
	$('#addCommentForm').submit(function(e){

 		e.preventDefault();
		if(working) return false;

		working = true;
		$('#submit').val('Working..');
		$('span.error').remove();

		/* Sending the form fileds to submit.php: */
		$.post('submit.php',$(this).serialize(),function(msg){

			working = false;
			$('#submit').val('Submit');

			if(msg.status){

				/*
				/	If the insert was successful, add the comment
				/	below the last one on the page with a slideDown effect
				/*/

				$(msg.html).hide().insertBefore('#addCommentContainer').slideDown();
				$('#body').val('');
			}
			else {

				/*
				/	If there were errors, loop through the
				/	msg.errors object and display them on the page
				/*/

				$.each(msg.errors,function(k,v){
					$('label[for='+k+']').append('<span class="error">'+
						v+'</span>');
				});
			}
		},'json');

	});

});

Starting from the top, we have the $(document).ready() call, which binds a function to the DOM content loaded event. The working variable acts as a flag, which tells the script if an AJAX request is in progress (thus preventing double posting).

In the callback function for the POST AJAX request, we check the status property to determine whether the comment was successfully inserted. If it was, we add the received markup to the page after the last comment with a slideDown animation.

If there were problems, we display the error messages by appending an error span to the appropriate label element (the for attribute of the label contains the id of the input that caused the error).

With this our Simple AJAX Commenting System is complete!

Conclusion

To be able to run this script on your server, you need to create the comments table in your MySQL database. You can do this by executing the SQL code found in table.sql from the SQL tab of phpMyAdmin. After this you need to enter your MySQL connection details in connect.php.

You are free to modify and use this code any way you see fit.

What do you think? How would you improve this script?

CSS3 Minimalistic Navigation Menu

As you have probably heard by now, CSS3 animations are a powerful tool, which enables you to create animations which run without the need of applying additional scripting to the page. What is even better, in the next generation of browsers we will have even more powerful tools, including 3D transformations (already present in Safari).

But what difference does it make for us today? At the moment only three browsers give you the ability to animate CSS properties – Chrome, Safari and Opera, which together take up only a small part of the browser market. Firefox is expected to soon join the club, and with the impending release of IE9, it suddenly makes sense to start leveraging this technique.

So today we are making something practical – a simple CSS3 animated navigation menu, which degrades gracefully in older browsers and is future-proofed to work with the next generation of browsers.

The XHTML

The menu is organized as an unordered list. This is the most suitable structure for a menu, as it provides an easy way to style the menu links and is semantically correct.

demo.html

<ul id="navigationMenu">
    <li>
	    <a class="home" href="#">
            <span>Home</span>
        </a>
    </li>

    <li>
    	<a class="about" href="#">
            <span>About</span>
        </a>
    </li>

    <li>
	     <a class="services" href="#">
            <span>Services</span>
         </a>
    </li>

    <li>
    	<a class="portfolio" href="#">
            <span>Portfolio</span>
        </a>
    </li>

    <li>
    	<a class="contact" href="#">
            <span>Contact us</span>
        </a>
    </li>
</ul>

Inside each li we have a hyperlink with a span inside it. By default these spans are hidden, and are only shown when you hover over the link. Each link has a unique class name, which is used to give it a unique background and style the inner span, as you will see in a moment.

CSS3 Animated Navigation Menu

CSS3 Animated Navigation Menu

The CSS

Once we have the basic structure in place, we can now move to creating the fancy CSS3 effects and styling. This works even on browsers, which do not support CSS3 transition animations (all browsers except Chrome, Safari and Opera, at the moment of this writing) albeit with less glitter. The menu is even perfectly usable in browsers as old as IE6.

styles.css – Part 1

*{
	/* A universal CSS reset */
	margin:0;
	padding:0;
}

body{
	font-size:14px;
	color:#666;
	background:#111 no-repeat;

	/* CSS3 Radial Gradients */
	background-image:-moz-radial-gradient(center -100px 45deg, circle farthest-corner, #444 150px, #111 300px);
	background-image:-webkit-gradient(radial, 50% 0, 150, 50% 0, 300, from(#444), to(#111));

	font-family:Arial, Helvetica, sans-serif;
}

#navigationMenu li{
	list-style:none;
	height:39px;
	padding:2px;
	width:40px;
}

For the styling of the body background, I first supplied a background color, which acts as a fallback, and then added two CSS3 radial gradients (for Firefox and Chrome/Safari respectfully) as background images. If the visitor’s browser does not support gradients, it will just ignore the rules and go with the plain background color.

You can see in the styles, that most of the rules are preceded by the id of the unordered list – #navigationMenu. This is to prevent collisions with the rest of your page styles, if you incorporate the menu into your site.

styles.css – Part 2

#navigationMenu span{
	/* Container properties */
	width:0;
	left:38px;
	padding:0;
	position:absolute;
	overflow:hidden;

	/* Text properties */
	font-family:'Myriad Pro',Arial, Helvetica, sans-serif;
	font-size:18px;
	font-weight:bold;
	letter-spacing:0.6px;
	white-space:nowrap;
	line-height:39px;

	/* CSS3 Transition: */
	-webkit-transition: 0.25s;

	/* Future proofing (these do not work yet): */
	-moz-transition: 0.25s;
	transition: 0.25s;
}

#navigationMenu a{
	/* The background sprite: */
	background:url('img/navigation.jpg') no-repeat;

	height:39px;
	width:38px;
	display:block;
	position:relative;
}

/* General hover styles */

#navigationMenu a:hover span{ width:auto; padding:0 20px;overflow:visible; }
#navigationMenu a:hover{
	text-decoration:none;

	/* CSS outer glow with the box-shadow property */
	-moz-box-shadow:0 0 5px #9ddff5;
	-webkit-box-shadow:0 0 5px #9ddff5;
	box-shadow:0 0 5px #9ddff5;
}

The CSS3 transition property is  a really powerful one. It enables you to animate changes that occur on a element when a pseudo properties take effect. For example here, when the user moves their mouse over a navigation link, the :hover pseudo-property takes effect, showing the span which is otherwise hidden.

Without the definition of a transition property, this change is instantaneous. But with a transition we can animate it. Here we are telling the browser, that the duration of the animation is 250 milliseconds. You can optionally specify a list of specific properties to be animated instead of all of them.

Transitions are currently only supported in webkit based browsers (Safari and Chrome), but the next version of Firefox is also expected to support them, so we future-proof the script by specifying a -moz-transition.

The CSS3 Transition

The CSS3 Transition

styles.css – Part 3

/* Green Button */

#navigationMenu .home {	background-position:0 0;}
#navigationMenu .home:hover {	background-position:0 -39px;}
#navigationMenu .home span{
	background-color:#7da315;
	color:#3d4f0c;
	text-shadow:1px 1px 0 #99bf31;
}

/* Blue Button */

#navigationMenu .about { background-position:-38px 0;}
#navigationMenu .about:hover { background-position:-38px -39px;}
#navigationMenu .about span{
	background-color:#1e8bb4;
	color:#223a44;
	text-shadow:1px 1px 0 #44a8d0;
}

/* Orange Button */

#navigationMenu .services { background-position:-76px 0;}
#navigationMenu .services:hover { background-position:-76px -39px;}
#navigationMenu .services span{
	background-color:#c86c1f;
	color:#5a3517;
	text-shadow:1px 1px 0 #d28344;
}

/* Yellow Button */

#navigationMenu .portfolio { background-position:-114px 0;}
#navigationMenu .portfolio:hover{ background-position:-114px -39px;}
#navigationMenu .portfolio span{
	background-color:#d0a525;
	color:#604e18;
	text-shadow:1px 1px 0 #d8b54b;
}

/* Purple Button */

#navigationMenu .contact { background-position:-152px 0;}
#navigationMenu .contact:hover { background-position:-152px -39px;}
#navigationMenu .contact span{
	background-color:#af1e83;
	color:#460f35;
	text-shadow:1px 1px 0 #d244a6;
}

In the last part of the styling, we specify 5 different designs for the navigation links. All the background images for the links are contained inside a single sprite file. They have a normal and a hover state one under another. When a hover occurs, the background is offset to show the appropriate version of the background image.

A PSD file is included in the downloadable archive, so you can customize the images as much as you like.

With this our minimalistic CSS3 navigation menu is complete!

Conclusion

Sooner or later, we are going to have a quick access to powerful, hardware accelerated graphics, right in the browser. When this day comes, a whole new world will open to web developers, and we will come even closer to building rich internet applications, which behave exactly like native apps.

Till then, we have to make the best with what we have, and slowly start adopting CSS3 techniques into our work.

What do you think? How would you improve this navigation menu?

Apple-like Retina Effect With jQuery

Apple has long applied a winning strategy in marketing – create well designed products, have a dedicated fan base, and let the hype build up before every product release.

This is also the case with the latest version of their iPhone. But what I found interesting is the term they coined – “Retina display” and the promo image accompanying it.

The image on apple.com that inspired it all

The image on apple.com that inspired it all

What made me wonder is if it was possible to turn this static image into a fully functional “Retina effect” with only jQuery and CSS. This is exactly what we are doing today. So grab the demo files from the button above and read on.

UPDATE: Seems Apple folks have also liked the idea, and they’ve implemented it on apple.com.

Step 1 – XHMTL

The markup for the effect is quite straightforward. You can see that we only have a number of divs and an image.

demo.html

<div id="main">

	<div id="iphone">
		<div id="webpage">
			<img src="img/webpage.png" width="499" height="283" alt="Web Page" />
			<div id="retina"></div>
		</div>
	</div>

</div>

The #iphone div displays the iphone frame. Inside it is the #webpage div with the screenshot of the webpage. The screenshot is actually displayed at half its original size, as we are using the same image for both the small version (displayed in the  iPhone) and big version, which is shown in the rounded tooltip.

Lastly we have the retina div, which is rounded with CSS3 and displays the big version of the webpage screeshot as its background as it is moved around with the mouse.

Putting it together

Putting it together

Step 2 – CSS

Moving to the CSS part of the tutorial. We are going to style the iphone, webpage and retina divs, so we make the awesome effect possible.

styles.css

#iphone{
	/* The iphone frame div */
	width:750px;
	height:400px;
	background:url('img/iphone_4G.png') no-repeat center center;
}

#webpage{
	/* Contains the webpage screenshot */
	width:499px;
	height:283px;
	position:absolute;
	top:50%;
	left:50%;
	margin:-141px 0 0 -249px;
}

#retina{
	/* The Retina effect */
	background:url('img/webpage.png') no-repeat center center white;
	border:2px solid white;

	/* Positioned absolutely, so we can move it around */
	position:absolute;
	height:180px;
	width:180px;

	/* Hidden by default */
	display:none;

	/* A blank cursor, notice the default fallback */
	cursor:url('img/blank.cur'),default;

	/* CSS3 Box Shadow */
	-moz-box-shadow:0 0 5px #777, 0 0 10px #aaa inset;
	-webkit-box-shadow:0 0 5px #777;
	box-shadow:0 0 5px #777, 0 0 10px #aaa inset;

	/* CSS3 rounded corners */
	-moz-border-radius:90px;
	-webkit-border-radius:90px;
	border-radius:90px;
}

#retina.chrome{
	/* A special chrome version of the cursor */
	cursor:url('img/blank_google_chrome.cur'),default;
}

#main{
	/* The main div */
	margin:40px auto;
	position:relative;
	width:750px;
}

By specifying an absolute positioning on the webpage div, we can apply the vertical and horizontal centering technique, effectively putting the webpage screenshot in the middle of the iphone frame.

The retina div is also assigned an absolute positioning, so it is possible to move it in the jQuery part of the tutorial by just specifying a top and left offsets. This div also has the webpage screenshot as its background (in its original size). Offsetting the background with the movement of the div creates the illusion that it magnifies the small screenshot below it.

The Retina Effect

The Retina Effect

The retina div also has applied a border radius with a value of exactly half its width, which turns it into a perfect circle (at least in browsers which support the border-radius CSS3 property – Chrome, Safari, Opera & Firefox).

And finally we hide the mouse pointer by providing a blank cursor file (Google Chrome fails to render completely blank cursors, so we provide a special 1px white cursor for it – at least it is better than nothing). The Opera browser completely ignores custom cursors and there are no workarounds, so users using this browser might not enjoy the full experience.

Step 3 – jQuery

If you can remember, a few months ago we did a tutorial here at Tutorialzine, in which we used jQuery to create a photoshoot effect. This time we are using a similar technique to make the “retina effect”, as seen on Apple’s web site.

And since we have all the styling in place, it is a matter of some JavaScript coding with the help of the jQuery library.

script.js

$(document).ready(function(){

	/* This code is executed on the document ready event */

	var left	= 0,
		top		= 0,
		sizes	= { retina: { width:190, height:190 },
				webpage:{ width:500, height:283 } },
		webpage	= $('#webpage'),
		offset	= { left: webpage.offset().left, top: webpage.offset().top },
		retina	= $('#retina');

	if(navigator.userAgent.indexOf('Chrome')!=-1)
	{
		/*	Applying a special chrome curosor,
			as it fails to render completely blank curosrs. */

		retina.addClass('chrome');
	}

	webpage.mousemove(function(e){

		left = (e.pageX-offset.left);
		top = (e.pageY-offset.top);

		if(retina.is(':not(:animated):hidden')){
			/* Fixes a bug where the retina div is not shown */
			webpage.trigger('mouseenter');
		}

		if(left<0 || top<0 || left > sizes.webpage.width ||
			top > sizes.webpage.height)
		{
			/*	If we are out of the bondaries of the
				webpage screenshot, hide the retina div */

			if(!retina.is(':animated')){
				webpage.trigger('mouseleave');
			}
			return false;
		}

		/*	Moving the retina div with the mouse
			(and scrolling the background) */

		retina.css({
			left				: left - sizes.retina.width/2,
			top					: top - sizes.retina.height/2,
			backgroundPosition	: '-'+(1.6*left)+'px -'+(1.35*top)+'px'
		});

	}).mouseleave(function(){
		retina.stop(true,true).fadeOut('fast');
	}).mouseenter(function(){
		retina.stop(true,true).fadeIn('fast');
	});
});

In the mousemove function, the current coordinates of the mouse are passed as e.pageX and e.pageY, but they are absolute with relation to the document. Subtracting the position offset of the website div, we end up with relative coordinates for the mouse, which are later used to position the retina div.

This, combined with the blank cursor we set up in the CSS part of the tutorial, creates the retina effect in pure JavaScript and CSS.

Conclusion

As the Flash / HTML5 debate heated up recently, people started looking for ways to achieve the same level of functionality that flash provides, without depending on an external plugin. This is possible for simple interactions, but we still have to make sure that the solution is cross-browser compatible, which is sometimes rather impossible (think of IE here).

Hopefully, today we made something that would easily rival an equivalent Flash solution.

What do you think? How would you improve this effect?

Creating a Facebook-like Registration Form with jQuery

Introduction

Facebook is a showcase of great UI design. And as it has become a major part of our lives, it has also raised the bar for web development, pushing developers to meet higher expectations.

This however has a good side – it challenges web developers and designers to better themselves and constantly improve their work.

In this tutorial, we are going to learn from the best, and create a facebook-like sign up form. So go ahead and download the demo files to start learning!

The XHTML

We start off by laying down the XHTML backbone, as seen in index.php in the demo files:

<div id="div-regForm">

<div class="form-title">Sign Up</div>
<div class="form-sub-title">It's free and anyone can join</div>

<form id="regForm" action="submit.php" method="post">
<table>
<tbody>
<tr>

	<td><label for="fname">First Name:</label></td>
	<td><div class="input-container">
	<input name="fname" id="fname" type="text" />
	</div></td>

</tr>
<tr>

	<td><label for="lname">Last Name:</label></td>
	<td><div class="input-container">
	<input name="lname" id="lname" type="text" />
	</div></td>

</tr>
<tr>

	<td><label for="email">Your Email:</label></td>
	<td><div class="input-container">
	<input name="email" id="email" type="text" />
	</div></td>

</tr>
<tr>

	<td><label for="pass">New Password:</label></td>
	<td><div class="input-container">
	<input name="pass" id="pass" type="password" />
	</div></td>

</tr>
<tr>

	<td><label for="sex-select">I am:</label></td>
	<td>
	<div class="input-container">
	<select name="sex-select" id="sex-select">
	<option value="0">Select Sex:</option>
	<option value="1">Female</option>
	<option value="2">Male</option>
	</select>

	</div>
	</td>
</tr>
<tr>
	<td><label>Birthday:</label></td>
	<td>

	<div class="input-container">

	<select name="month">
	<option value="0">Month:</option>
	<?=generate_options(1,12,'callback_month')?>
	</select>

	<select name="day">
	<option value="0">Day:</option>
	<?=generate_options(1,31)?>
	</select>

	<select name="year">
	<option value="0">Year:</option>
	<?=generate_options(date('Y'),1900)?>
	</select>

	</div>

	</td>

</tr>
<tr>

	<td>&nbsp;</td>
	<td><input type="submit" class="greenButton" value="Sign Up" />
	<img id="loading" src="img/ajax-loader.gif" alt="working.." />
	</td>

</tr>

</tbody>
</table>

</form>

<div id="error">
&nbsp;
</div>

</div>

We begin by creating a div container for the form, to which we assign the id div-regForm. In it we are going to position the various form components.

Later we create the headings of the form, properly styled with CSS, as you’ll see later.

Then we have the form itself. A note here would be to remember that the form does not actually get submitted by its own – its all done via AJAX, which means that it doesn’t really matter what you are going to put in the method and action attributes.

Inside the form, we position a table, which will allow us to easily create a grid layout for the form labels and fields. There’s been a debate on using tables in this manner since div layout came to fashion, but mind you that facebook itself is using the same technique, which automatically wins it for me.

Every input field has a respective label element serving as a, you guessed it – a field label. We are even using the for attribute, which means that by clicking the label, you select the textbox on the right.

Next comes a select box and after this we have 3 very interesting lines, that I’ve highlighted for you. We use a neat little PHP function to generate all the option elements that go into the select boxes comprising the selection of a birth date. We will talk about this in a minute.

Later we have the Sign Up button, and a little gif, which is hidden by default and shown only when AJAX requests are in progress.

The last div element is our error container, also hidden by default.

Our facebook-like form

Our facebook-like registration form

The CSS

In order to convert our plain XHML coding into something eye-catching and facebook-likey, we need some serious styling.

Lets take a look at our CSS, as defined in demo.css.

/* Page styles */

body,h1,h2,h3,p,td,quote,small,form,input,ul,li,ol,label{
	margin:0px;
	padding:0px;
}

body{
	margin-top:20px;
	font-family:Arial, Helvetica, sans-serif;
	color:#51555C;
	height:100%;

	font-size:11px;
}

/* Form styles */

input,select{
	padding:3px;
	color:#333333;

	border:1px solid #96A6C5;
	margin-top:2px;
	width:200px;
	font-size:11px;
}

select{
	width:auto;
	padding:2px;
}

.formline{
	padding:3px;
}

label{
	font-size:11px;
	text-align:right;
}

table{
	width:300px;
}

td{
	font-size:11px;
}

.input-container{
	padding:1px;
}

#div-regForm,.registered{
	border:3px solid #eeeeee;
	padding:15px;

	background:url(img/bg.jpg) repeat-x #cbd4e4;
	color:#203360;

	margin:30px auto 40px auto;
	width:400px;
}

.form-title,
.form-sub-title{
	font-size:20px;

	font-family:"Lucida Grande",Tahoma,Verdana,Arial,sans-serif;
	font-size:20px;
	font-weight:bold;
}

.form-sub-title{
	font-weight:normal;
	padding:6px 0 15px 0;
}

.greenButton{
	width:auto;
	margin:10px 0 0 2px;
	padding:3px 4px 3px 4px;
	color:white;
	background-color:#589d39;
	outline:none;
	border:1px solid #006600;
	font-weight:bold;
}

.greenButton:active{
	background-color:#006600;
	padding:4px 3px 2px 5px;
}

#loading{
	left:10px;
	position:relative;
	top:3px;
	visibility:hidden;
}

#error{
	background-color:#ffebe8;
	border:1px solid #dd3c10;
	padding:7px 3px;
	text-align:center;
	margin-top:10px;
	visibility:hidden;
}

Lines 1-6 are where we reset some of the XHTML elements, to ensure that they appear the same in all browsers.

We continue with styling the body section and start styling the form elements.

The first elements we style are input and select. Select shares most of its styling with input, but also differs in width and padding, so we put an additional set of styles to cover them.

Its mostly widths and paddings down to line 81, where we style our sign up button. We have styles for both the normal, non-pressed state, and the active, pressed state. What the active state does, is that it moves the text label of the button to the bottom – right with one pixel, while darkening the background, which gives the illusion of the button being pressed.

The last two styles are #loading and #error, which select the respective elements by their ID’s and hide them with visibility:hidden by default. We will only show them with jQuery when it is appropriate.

The PHP code

Remember, when a few minutes ago I mentioned about the generate_options PHP function?
Here it is, taken right from our functions.php file:

function generate_options($from,$to,$callback=false)
{
	$reverse=false;

	if($from>$to)
	{
		$tmp=$from;
		$from=$to;
		$to=$tmp;

		$reverse=true;
	}

	$return_string=array();
	for($i=$from;$i<=$to;$i++)
	{
		$return_string[]='
		<option value="'.$i.'">'.($callback?$callback($i):$i).'</option>
		';
	}

	if($reverse)
	{
		$return_string=array_reverse($return_string);
	}

	return join('',$return_string);
}

function callback_month($month)
{
	return date('M',mktime(0,0,0,$month,1));
}

/* and here is how we use it (taken from our XHTML code above):
generate_options(1,31);				// generate days
generate_options(date('Y'),1900);			// generate years, in reverse
generate_options(1,12,'callback_month');		// generate months
*/

This is a neat little peace of code. Now lets explain what it does.

The first thing you notice in our generate_options function are the parameters $from and $to for the range of options to be generated (days, months and years in our case), and an optional parameter $callback, which is a callback function (more on that in a moment).

Inside the function, we define $reverse as false. If we use the function in the following manner: generate_options(1,31), we are just generating a series of option elements and outputting them in the same order. If we switch the positions of the parameters the output is in reverse order.

This is exactly what we do in line 5. We check if the range of $from and $to is reversed and setting $reverse to true. But in the same time, we are exchanging their values, so that we use the same for construct as in our normal generation.  If we haven’t done this we would have had to write another for to do this job.

Next we fill an array called $return_string with the generated options, reversing it if necessary and outputting it as string by joining the array elements.

Remember the $callback I talked about earlier? Here comes its part. On line 18 we check if  a $callback was supplied – as in generate_options(1,12,’callback_month’), and if it was, we execute the function and supplying the current $i counter’s value. In practice, it is the same as if we were doing callback_month($i).

Later we have the callback_month function, which we use as a callback in the month generation. It basically takes an integer parameter (our $i above), and returns the month name as string.

The jQuery source

Ok, now that we have completed the form’s looks, we need to be able to send the data over to the PHP backend where it is processed. For this purpose, we are using jQuery, which you can include in page by putting these lines of code in your page’s head section:

<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>

The library is automatically included from Google’s CDN. After that you can use jQuery’s ajax method.

Here is the code located in script.js.

$(document).ready(function(){

	$('.greenButton').click(function(){

		register();

	});

	$('#regForm').submit(function(e) {

		register();
		e.preventDefault();

	});

});

function register()
{
	hideshow('loading',1);
	error(0);

	$.ajax({
		type: "POST",
		url: "submit.php",
		data: $('#regForm').serialize(),
		dataType: "json",
		success: function(msg){

			if(parseInt(msg.status)==1)
			{
				window.location=msg.txt;
			}
			else if(parseInt(msg.status)==0)
			{
				error(1,msg.txt);
			}

			hideshow('loading',0);
		}
	});

}

function hideshow(el,act)
{
	if(act) $('#'+el).css('visibility','visible');
	else $('#'+el).css('visibility','hidden');
}

function error(act,txt)
{
	hideshow('error',act);
	if(txt) $('#error').html(txt);
}

The first lines of code in $(document).ready get executed after the page has loaded and bind our register() function with the button’s onclick event and our form’s onsubmit event utilizing the preventDefault() method in order to stop the form from being submitted.

And here is the simplicity of the $.ajax method (line 23) – in a few lines of code we send to submit.php by POST all of regForm’s fields (regForm is the ID of our registration form). We receive a response in JSON format (for more on that later) which gets processed by the function given in success. In this example (lines 30-37) we process the returned object and decide whether to show an error, or redirect to a registered-user-only page.

Later we have the hideshow() function which hides or shows an element on the page (for example the rotating gif animation on our form) and the error function which manages the displaying of errors on the form.

But how do we decide whether the information in the form is correct, and where to redirect the user if it is?

This is done in submit.php:

// we check if everything is filled in

if(empty($_POST['fname']) || empty($_POST['lname']) || empty($_POST['email']) || empty($_POST['pass']))
{
	die('{status:0,txt:"All the fields are required"}');
}

// is the sex selected?

if(!(int)$_POST['sex-select'])
{
	die('{status:0,txt:"You have to select your sex"}');
}

// is the birthday selected?

if(!(int)$_POST['day'] || !(int)$_POST['month'] || !(int)$_POST['year'])
{
	die('{status:0,txt:"You have to fill in your birthday"}');
}

// is the email valid?

if(!(preg_match("/^[\.A-z0-9_\-\+]+[@][A-z0-9_\-]+([.][A-z0-9_\-]+)+[A-z]{1,4}$/", $_POST['email'])))
	die('{status:0,txt:"You haven\'t provided a valid email"}');

echo '{status:1,txt:"registered.html"}';

Here we check some of the most typical error situations. Every message, that we output, has to be formatted as a javascript object with status and txt properties. They are accessible in our AJAX as msg.txt and msg.status. The main idea here is that errors return a status with a value of 0 and a successful registration returns a status 1, with txt being a URL of a resource accessible only for registered users.

*Note that you’ll have to add code for inserting new database records, creating sessions and a few more error checks on your own, depending on your needs.

And with that our form is finished.

Conclusion

Today we created a fancy looking and functional registration page, inspired by no other than facebook itself. We successfully used jQuery and the $.ajax method to create a real time, asynchronous registration form, complete with error checking and browser redirects.

Create a page peel effect with jQuery

Introduction

You have seen the effect – a website that has in its corner an animation that looks like a page from a book is being opened (this same effect is used on the BloggyBits website). This is called a page peel effect and is an increasingly popular design element. Up till recently, it was quite a challenge to create something like similar. Luckily for us, now our favorite java script library jQuery has a plugin to do just that – the pagePeel plugin.

The page peel effect - a solution to banner blindness

The page peel effect - a solution to banner blindness

The includes

In order for this to work we will need to include the jQuery library and the pagePeel plugin itself in the HEAD section of our HTML document.

<head>
<script type="text/javascript" src="/jquery.js"></script>
<script type="text/javascript" src="/pp/jQuery.pagePeel.js"></script>
</head>

Here jquery.js is in the same directory as our html file and jQuery.pagePeel.js is in the pp directory. We have to include them in that order.

After that we have to create a DIV, that is going to be converted to our page corner. It is best that the DIV is positioned in at the bottom of the <body> section, before the closing </body> tag.

...........
<div id="pagePeel"></div>
</body>
</html>

The code

Now that we have all the java script files necessary we need to write the code that is going to create our page peel effect. The code should be positioned in the head section, after the included files.

<script type="text/javascript">
$(document).ready(function() {
	$('#pagePeel').pagePeel({
		bigAd: '/images/bloggy_page_peel_light.gif',
		bigSWF: '/pp/page-peel-big.swf',
		adLink: 'http://www.stepinto.net',
		adLinkTarget: '_blank'
	});
}
</script>

$(document).ready is a special jQuery method that allows us to have code executed after the page has loaded.

Inside, on line 2 we have the special method, .pagePeel that was included in the page with the pagePeel library. We are basically converting the DIV with an id of pagePeel into the page corner animation.

The method takes an object as a parameter and you can see from line 3 to line 6 that we have used several properties. bigAd for the background image of the ad, bigSWF is the address of the default swf flash file that comes with the plugin (it creates the page peel effect), adLink for the address that the animation leads to and adLinkTarget is for the target (in this case it opens in a new window).

You maybe wonder where do we specify the size and the position? The default size is 500×500 pixels and we don’t need to specify it additionally (only if we want a different size). For the purpose you can add the following properties: smallWidth and smallHeight (for the small, inactive version) and bigWidth and bigHeight for the full version. There also more interesting properties for which you can refer to at the plugin’s homepage.

You can change the background graphic anyway you like. It is important that it is the same size as the size of the full version of the animation.

Done! You can see a working demo at BloggyBits.com.

Conclusion

That concludes this tutorial. Creating a page flip animation on a web page has never been that easy. This is an original way to present advertisement, or promotions on your site. The best thing about it is that people have not developed the so called banner blindness (ignoring banner or text advertisement on web sites) for this kind of format. At least for now that is.

If you liked this tutorial you can subscribe to our RSS feed or follow us on twitter.

Powered by Gewgley