Advertisement

You Still Can't Create a jQuery Plugin?

by

It's tough. You read tutorial after tutorial, but they all assume that you know more than you actually do. By the time you're finished, you're left feeling more confused than you initially were. Why did he create an empty object? What does it mean when you pass "options" as a parameter? What do "defaultsettings" actually do?

Never fear; I'm going to show you exactly how to build your own "tooltip" plugin, at the request of one of our loyal readers.

Why Would I Create a Plugin in the First Place?

It might help to think of plugins in the same way as you would functions. Have you ever found yourself repeating the same procedures
for site after site? Perhaps you've created your own tabbing system that you like to use. Rather than writing the same code time and
time again, wouldn't it be easier if we could turn your long code block into one line? That's essentially what a plugin does for us.
It allows for reusability, which is paramount - if you want to keep your hourly wage high!

Step 1: The Markup

Create a new project in your favorite code editor, and paste in the following html.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<link rel="stylesheet" href="css/default.css" />
	<title>You Still Can't Create a jQuery Plugin?</title>
</head>
<body>

<div id="container">
	<p>
		The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable <a href="#" class="tooltip" title="This is my title">English</a>. Many desktop publishing packages and web 
		page <a href="#">editors</a> now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. <a href="#">Various</a> versions have evolved <a href="#" class="tooltip" title="The other day, I bla bla with WordPress.">over the years</a>, sometimes by accident, sometimes on purpose (injected humour and the like).
	</p>	
</div><!--end container-->

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

<script type="text/javascript">
	$('a.tooltip').tooltip({
		rounded: true
	});
</script>		

</body>
</html>

Explanation

This code is fairly simple; so it doesn't require too much of an overview.

  • Reference a CSS file which we'll create shortly. This will allow us to style our tooltip.
  • Add some generic text that contains a few anchor tags. Notice that some of them contain a class of "tooltip".
    This is important!
  • Import jQuery from Google's CDN.
  • Call our tooltip with jQuery. Don't worry about this yet. I'll explain it shortly.

Step 2: Calling the Plugin

I typically find that it's easier to build my plugin if I first determine how I'm going to call it. Consider the following code.

<script type="text/javascript">
	$('a.tooltip').tooltip({
		rounded: true
	});
</script>

I've decided that I want the name of my plugin to simply be called "tooltip". I've also determined that I want to be able to pass in a few
parameters in order to customize my tooltip slightly. This will purely be optional for the user though. If preferred, he or she could just type:

<script type="text/javascript">
	$('a.tooltip').tooltip();
</script>

In this instance, the default options will be used.

image

Step 3: Building the Plugin

image

So now that we know how the plugin will be called, let's go ahead and build it! Create a new file in your project, and name it "jQuery.tooltip.js". There are certain naming conventions when creating a plugin that will potentially be used by others. We begin by writing 'jQuery', followed by the name of our plugin. However, I'm sure that you realize that thousands of people have created their own tooltip plugin. You might determine that it's necessary to preface "tooltip" with a unique string; perhaps your initials. In any event, it doesn't matter too much. You're free to name your plugin however you wish.

Passing jQuery as a Parameter

(function($){
  ...code
})(jQuery);

We must first ensure that the "$" symbol doesn't get us into any trouble. What if we were also using other Javascript libraries in our application? Have you ever thought about that? In such cases, we would need to change every "$" in our document to "jQuery". The only problem is that this is a rather sloppy solution. Instead, let's create a self invoking anonymous function, and pass "$" as an alias. That way, we're free to use "$" as much as we like without having to worry about any conflicts.

Naming the Plugin

$.fn.tooltip = function(options) {

Within our wrap, we need to declare our new plugin and make it equal to the following function. We can accomplish this task by typing jQuery.fn (short for prototype).the name of our plugin.

Remember when I specified that the user should be able to make small modifications to the tooltip? Those modifications will be stored within the "options" parameter.

Setting the Defaults

var 
 defaults = {
	background : '#e3e3e3',
	color : 'black',
	rounded: false
},

It's probable that the user might not specify any options. So, we'll set our own "default" options. Because this is a tutorial for beginners, we won't do too much here. Mostly, this is demonstrating what's possible. We're allowing the user to select a background color, color, and whether or not the tooltip is rounded.

Later in our code, when we wish to access these values, we can simply type: defaults.rounded.

Merging the Defaults With the Options

So how can we determine if the user adds his or her own options? The answer is that we must merge 'defaults' with 'options'.

settings = $.extend({}, defaults, options);

We've created a new variable called "settings", and have told jQuery to merge 'defaults', and 'options', and place the results into a new object. When merging, the user selected 'options' will take precedence over the defaults.
You might be wondering why I haven't added "var" before settings. Technically, it's not required, though it's considered best practice. If you'll scroll up a bit, you'll see that after our defaults object, I added a comma. When doing so, we can continue creating more variables without the need for 'var' each time. For example...

var joe = {};
var is = {};
var good = {};

can become....

var joe = {},
    is = {},
	good = {};

For Each...

this.each(function() {
	var $this = $(this);
        var title = this.title;

It's important to keep in mind that the wrapped set that the user passes to this plugin may contain many elements - not just one. We should make sure that we use a for each statement - or jQuery's "each" method - to cycle through the code.

We begin our new function by caching $(this). Not only will it save us a few characters, but it will also speed things up, ever so slightly. It's not required, but is considered to be good practice. Many people ask, "Why do you add the dollar sign in front of your variable?". We do this to remind ourselves that we're working with the jQuery object. By doing so, I remember that I can call things like: $this.click();. Next, I'm storing the value of the title attribute into a variable called "title". You'll see why we must do this in a moment.

Error Checking

if ($this.is('a') && $this.attr('title') != '') {
        this.title = '';
	$this.hover(function(e){

It's easy to forget what $this is referring to. When you do forget, just go back to the way you're calling the "tooltip" method.

$('a.tooltip').tooltip();

We can see here that $this is referring to each anchor tag that has a class of "tooltip".

Back to the code; if the element in the wrapped set is, in fact, an anchor tag, AND its 'title' attribute is not empty, then run some code. This is rather self explanatory. I don't want to run a bunch of code if the user accidentally passes in an image. Additionally, I don't want to create my tooltip box if there isn't anything to show!

Within my "if" statement, I'm setting the "title" attribute of the anchor tag equal to nothing. This will stop the browser's default tooltip from loading. Next, I'm adding an event listener for when $this, or the anchor tag, is hovered over. When it is, we create a new function and pass the event object as a parameter. Don't worry about this for now, I'll explain later.

Creating Our Tooltip

image
var title = $this.attr('title');			
$('<div id="tooltip" />')
   .appendTo('body')
   .hide()
   .text(title)
   .css({
	 backgroundColor: settings.background,
	 color: settings.color,
	 top: e.pageY + 10,
	 left: e.pageX + 20
   })
   .fadeIn(350);

I've created a variable called 'title' and made it equal to whatever value is contained within the anchor tag's 'title' attribute. This isn't necessary really - but it helps me.

When we add html tags to our jQuery statement, we can actually create a new element, rather than retrieve one. We're creating a new div tag with an id of 'tooltip'. Next, we'll use jQuery's wonderful chaning abilities to perform a bunch of procedures with ease.

  • .appendTo('body') : Take the element that we just created and append it to the body element, just before the closing 'body' tag.
  • .hide() : I want our tooltip to fade in. To achieve this effect, it must first have its display set to none.
  • .text(title) : We've created our div tag; but it's still empty. Let's add some text. We specify that whatever was contained in the title attribute should be placed into this div.
  • .css({..}) : We're setting the background-color, color, top, and left positions of our tooltip with CSS. Remember earlier when we merged the default settings with the user's selected options into a new 'settings' object? That will come into play now. The background-color is equal to 'settings.background'; color is 'settings.color'. Finally, we need to declare where on the page the tooltip should pop up. We can use the event object. It contains two properties called 'pageY', and 'pageX'. These contain the values of the exact coordinates of where the anchor was hovered. To add a bit of padding, we'll add 10px and 20px, respectively.
  • .fadeIn(350) : Over the course of about a third of a second, our tooltip will fade in.
if (defaults.rounded) {
	$('#tooltip').addClass('rounded');
}

By default, our 'rounded' option is set to 'false'. However, if 'defaults.rounded' returns true (meaning that the user added this parameter), we need to add a class of 'rounded' to our CSS file. We'll create that file soon.

Mouse Out

, function(){
	  // mouse out
	  $('#tooltip').hide();
  });

"Hover" accepts two functions: mouse over, and mouse out. We've added the appropriate code for when the user mouses over our selected anchor tag. But, we also need to write some code that removes the tooltip once the mouse has left the anchor tag.

Mouse Move

It would be nice to force the tooltip box to shift when our mouse moves. Let's implement that quickly.

$this.mousemove(function(e) {
	$('#tooltip').css({
		top: e.pageY + 10,
		left: e.pageX + 20
	});		
});

When the mouse moves over the anchor tag, create a function and, once again, pass the event object as a parameter. Find the 'tooltip' element, and adjust its CSS. You should realize that it's the exact code as we previously wrote. We're simply resetting these values to the new ones. Nifty, eh?

Allow for Chaning

We must allow the user to continue chaining after he's called "tooltip". For example:

$('a.tooltip').tooltip().css(..).remove();

In order to allow for this chaining, we need to return 'this'. Just before your closing curly brace, add "return this;".

Plugin Complete!

You've just successfully created a fully working plugin. It's not too advanced, but it allowed us to review some key features. Here is the final code.

Final jQuery

(function($){
	$.fn.tooltip = function(options) {
		
		var
		  defaults = {
		  	background: '#e3e3e3',
			color: 'black',
			rounded: false
		  },
		  settings = $.extend({}, defaults, options);
		  
		  this.each(function() {
		  	var $this = $(this);
			var title = this.title;
			
			if($this.is('a') && $this.attr('title') != '') {
				this.title = '';
				$this.hover(function(e) {
					// mouse over
					$('<div id="tooltip" />')
					  .appendTo('body')
					  .text(title)
					  .hide()
					  .css({
					  	backgroundColor: settings.background,
						color: settings.color,
						top: e.pageY + 10,
						left: e.pageX + 20
					  })
					  .fadeIn(350);
					  
				  if(settings.rounded) {
				  	$('#tooltip').addClass('rounded');
				  }
				}, function() {
					// mouse out
					$('#tooltip').remove();
				});	
			}
			
			$this.mousemove(function(e) {
				$('#tooltip').css({
					top: e.pageY + 10,
					left: e.pageX + 20
			     });
			});
		  });
		  // returns the jQuery object to allow for chainability.
		  return this;
	}
})(jQuery);
image

Step 4: CSS

image

The last step is to add just a bit of CSS to prettify our tooltip. Create a new CSS file called 'default.css', and paste the following in.

#tooltip {
	background: #e3e3e3 url(../images/search.png) no-repeat 5px 50%;
	border: 1px solid #BFBFBF;
	float: left;
	font-size: 12px;
	max-width: 160px;
	padding: 1em 1em 1em 3em;
	position: absolute;
}

.rounded {
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

Nothing too complicated here. I'm referencing a background image that contains the standard 'search' magnifying glass. You're welcome to download the source code to use it. Additionally, I've added a bit of padding, a maximum width, and a font-size. Most of this comes down to preference. You're free to edit it however you wish.

The only vital property is "position: absolute;". Earlier, we set the "top" and "left" values with jQuery. In order for that positioning to take effect, we must set the position to absolute! This is important. If you don't do so, it won't work.

Thanks for Reading

To all those who have emailed me asking for this tutorial, I hope it's helped in some way. If I didn't explain myself well enough, be sure to view the associated screencast, and/or ask a comment. I'll try to help you to the best of my ability. Until next time...

Ready For Level 2?

If you have a solid grasp of the techniques presented in this tutorial, you're ready to move on! Consider signing up for a Net Plus membership to view an advanced tutorial, by Dan Wellman, that will teach you how to build a more complicated jQuery plugin. It also comes with an associated screencast. Be sure to sign up now!

  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.