"Tipped": jQuery tooltip plugin

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

"Tipped": jQuery tooltip plugin

Post by pickle »

Update 5: Created an official jQuery plugin project.
Update 4: The boss gave me the green light to release this open-source. The project page is here
Update 3: Reposition tooltip at top left before width calculation for repositioning done. This prevents inline elements from being squished.
Update 2: Fixed the showing/hiding of the "Close" button if there are tips with both "hover" and "click" mode
Update: Added ability to turn off the default tooltip that appears when an element has a title. Also added better creation of #tipped

Hi all,

Just finished version 2 of this baby & thought I'd throw it to the sharks. I created this plugin because nothing I found allowed me the flexibility I needed.

Features:
  • Tooltip can be triggered by hovering, or made sticky by clicking
  • Tooltip content can be provided as a string, pulled from the 'title' attribute, a jQuery object, returned from a callback function, or requested via AJAX
  • AJAX requests can be cached *this was one of the main reasons I made my own plugin
  • Tooltips can show a loading image while waiting for the AJAX request to be returned *this was the other main reason
  • Plugin ensures the tip appears fully on screen
Todo:
If the content of the tip is taller or wider than the viewport, restrict the dimensions of the tip to the viewport, and provide scrollbars

Usage:

Code: Select all

<input type = "hidden" name = "secret" value = "something" /><img src = "/path/to/some/image.png" alt = "Image" id = "tipMe" title = "I'm being tipped" />

Code: Select all

#tipped{
  /* Add whatever styles you want to make the tip look good, but these 2 properties must be defined */  
  position:absolute;
  display:none;
}
#tipped-closer-wrapper{ /* This is the id given to the <div/> around close button */ }
#tipped-closer{ /* This is the id given to the <span/> around the provided close text/html */ }

Code: Select all

//Can be as simple as:
$("#tipMe").tipped();//will provide a hover triggered tooltip that uses the 'title' attribute 

//or as useful as:
$("#tipMe").tipped({
  ajaxType:'GET',
  closer: 'Click me to close the tooltip',
  mode: 'click',
  params:function($trigger){
    return {  secret:$trigger.siblings('input:first').val()};  
  },
  source:'url',
  throbber:'/path/to/loading/graphic.gif',
  url:'encodeSecret.php'});
The last example will:
  1. Show the tip when #tipMe is clicked
  2. Show the tooltip with "graphic.gif" displayed
  3. Generate an AJAX request to "encodeSecret.php?secret=something"
  4. Show the result once its returned
  5. Provide a link labelled "Click me to close the tooltip" which will hide the tip when clicked

Code comes in at 7.97K/ 2.29K (Google Closure compiler)

Code: Select all

/******
 * Tipped: A tooltip plugin for jQuery
 * http://www.augustana.ualberta.ca/tls/help/foss/tipped
 *
 * Copyright 2010, University of Alberta
 *
 * Tipped is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Tipped is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Tipped.  If not, see <http://www.gnu.org/copyleft/lesser.html>
 *
 * v1.3.3:	- Became a good jQuery citizen and return the jQuery object from tipped() so it supports chaining
 *			- Fixed a bug that emptied out the title stored in data(), if tipped() is called
 *			  on an element twice
 * v1.3.2:	Fixed 'title' based tips that were trying to show the title from the attribute after it was emptied out
 * v1.3.1:	Did some stuff
 * v1.3:	Reposition tooltip at top left before width calculation for repositioning done.  This
 *			prevents inline elements from being squished.
 * v1.2:	Fixed showing/hiding of the "Close" button if there are tips with both "hover" and "click" mode
 * v1.1: 	- Added turning off of default tooltip that appears when an elelment has a title
 *			- #tipped element is now created explicitely as an window variable - fixes a problem with Safari
 * v1.0: 	Initial release
 */
(function($) {
	/******
	/*	Options
		
			ajaxType:	The type of HTTP request to make.
				Possible values: Anything $.ajax accepts (usually 'GET' or 'POST')
				Default: 'POST'
			
			cache:		Whether or not to cache AJAX requests.  Cache is based on URL, not URL + data, so if 
						you are making multiple requests to the same URL with different data, turn off cache
				Default: false
			
			closer:		The HTML to display when a tip is to be manually closed (ie: when triggered by a click).  
						All text in 'closer' will be injected inside another element that has the close listener
				Default: 'Close'
			
			marginX:	The pixels to the right of the element that the tip should appear.  This amount will be
						overridden if necessary to ensure the entire tip shows on the screen.
				Possible values: Any integer.  Negative numbers will position the tip to the left of the right
								 edge of the triggering element
				Default: '10'
				
			marginY:	The pixels to the bottom of the element that the tip should appear.  This amount will be
						overridden if necessary to ensure the entire tip shows on the screen.
				Possible values: Any integer.  Negative numbers will position the tip above of the bottom
								 edge of the triggering element
				Default: '10'
				
			mode:		The type of tip to make.  'Hover' shows and hides on hover, 'Click' is triggered with a
						click and requires clicking of the closer to go away
				Possible values: 'hover', 'click'
				Default: 'hover'
				
			params:		An object representing the parameters to send along with an AJAX request as 'data'
				Possible values:
					A callback: Data passed will be the object returned from this function.  Function will be passed
								a jQuery object representing the triggering element
					An object: Will be used as the data
				Default: {}
				
			source:		The source of the value to display.
				Possible values: 
					'title':	Value to display will be pulled from the 'title' attribute of the triggering element
					A callback: Value to display will be returned from the callback function.  Function will be passed
								a jQuery object representing the triggering element
					'url':		An AJAX request will be made to the address specified by the 'url' option
					Any other string:	Will be displayed
				Default: 'title'
				
			throbber:	The URL to the image to display while the AJAX request is being sent.  If blank, no throbber
						will be shown.
				Default: ''
				
			url:		The web address to make the AJAX request to.  Unused if 'source' is not 'url'
	*/
	
	var defaults = {
		ajaxType:'POST',
		cache:false,
		cached:{},
		closer:'Close',
		marginX:10,
		marginY:10,
		mode:'hover',
		params:{},
		source:'title',
		throbber:'',
		url:''
	};
	
	//create single tooltip
	window.$tip = {};
	window.$tip_content = {}

	$(document).ready(function(){
		$tip = $("#tipped").length ? $("#tipped") : $('<div id = "tipped"><div id = "tipped_content"></div></div>').appendTo(document.body).data('showing',false);
		$tip_content = $("#tipped_content");
	});

	$.fn.tipped = function(settings){
		this.each(function(i){
			
			$target = $(this);//shortcut
			
			//store settings
			settings = $.extend({},defaults,settings);
			$target.data('tipped',{settings:settings});			
			
			//2 modes act differently
			if(settings.mode == 'hover')
				$target
					.mouseover(function(){
						$.fn.tipped.showTip($(this));
					})
					.mouseout(function(){
						$.fn.tipped.hideTip($(this));
					});
			else if(settings.mode == 'click')
			{
				//add closer if necessary
				if($("#tipped-closer").length == 0)
					$tip.append('<div id = "tipped-closer-wrapper"><span id = "tipped-closer">'+settings.closer+'</span>');
					
				$target.click(function(){
					$this = $(this);
					$.fn.tipped.showTip($this);
					$("#tipped-closer").click(function(){
						$.fn.tipped.hideTip($this);
					});
				});
			}	
		});
		
		return this;
	};
	
	/**
	 * Function: showTip()
	 * Purpose: To initiate the showing of a tip.
	 * Parameters: $target: a jQuery object that has had a tip bound to it.  Tipped uses
	 *                      the settings associated with the $target to determine what to display
	 */
	$.fn.tipped.showTip = function($target)
	{
		//shortcuts
		var settings = $target.data('tipped').settings;
		var cached = $tip.data('cached');

		//manage the closer
		if(settings.mode != 'click')
			$("#tipped-closer-wrapper").hide();
		else
			$("#tipped-closer-wrapper").show();

		//hide the original title
		if($target.data('tipped').title === undefined)
		{
			$target.data('tipped',$.extend($target.data('tipped'),{title:$target.attr('title')}));
			$target.attr('title','');
		}

		//AJAX
		if(settings.source === 'url')
		{
			//if we're not caching, retrieve the value
			if(!settings.cache || cached === undefined || cached[settings.url] === undefined)
			{
				//set parameters
				var data = {};
				if(typeof settings.params == 'function')
					data = settings.params($target);
				else if(typeof settings.params == 'object')
					data = settings.params;
					
				$.ajax({
					beforeSend:function(){
						show($target,'<img src = '+settings.throbber+' alt = "Loading..." />');
					},
					data:data,
					error:function(){
						show($target,'Unable to retrieve contents');
					},
					success:function(display){
						if($tip.data('showing'))
							show($target,display);
						
						//cache results if necessary
						if(settings.cache)
						{
							var newCache = new Object;
							newCache[settings.url] = display;
							cached = $.extend(cached,newCache);
							$tip.data('cached',cached);
						}
					},
					type:settings.ajaxType,
					url:settings.url
				});
				return;
			}
			//otherwise, show the cached copy
			else
			{
				show($target,cached[settings.url]);
				return;
			}
		}
				
		
		var value = '';
		
		//'title' attribute
		if(settings.source === 'title')
			value = $target.data('tipped').title;
		
		//any other string
		else if(typeof settings.source == 'string')
			value = settings.source;
			
		//custom function
		else if(typeof settings.source == 'function')
			value = settings.source($target);
		
		//jQuery object
		else if(typeof settings.source == 'object')
			value = settings.source.html();
		
		show($target,value);
	}
	
	/*
	 * Function: hideTip()
	 * Purpose: To hide the tip
	 * Parameters: $target:	a jQuery object representing the element that triggered the tip
	 */
	$.fn.tipped.hideTip = function($target)
	{
		$target.attr('title',$target.data('tipped').title);
		$tip.data('showing',false).data('original','').hide();
		$tip_content.html('');
	}
	
	
	/*
	 * Function: getTrigger()
	 * Purpose: To provide access to the element that triggered the tip.  Useful for 
	 *          clicked tips that need to know who triggered them
	 *
	 * Access with: $.getTrigger()
	 */
	$.extend({
		getTrigger:function(){
			return $tip.data('original');
		}
	});

	/*
	 * Function: show()
	 * Purpose: To actually show the tip
	 * Parameters: $target: The element (wrapped in a jQuery object) that triggered the showing of this tip
	 *			   value: The HTML to place into the tip
 	 *
	 * Note: This function is private
	 */
	function show($target,value)
	{
		$tip_content.html(value);
		setPosition($target)
		$tip.data('showing',true).data('original',$target).show();
	}
	
	
	/*
	 * Function: setPosition()
	 * Purpose: To set the position of the tip.  This function is called after the content of the tip
	 *          is set, allowing the function to make a dynamic decision about the position of the tip
	 *			
	 *			The tip is always displayed fully on the screen & will be moved to ensure that.
	 * Parameters: $elem:	a jQuery object representing the element relative to which the tip is to be positioned.
	 *
	 * Note: This function is private
	 */
	function setPosition($elem)
	{
		var settings = $elem.data('tipped').settings;		

		//position tip in the top left corner, so full, proper width gets calculated
		$tip.css({left:0,top:0});

		//determine element position on screen
		var elemPos = $elem.offset();
		var posX = elemPos.left + $elem.outerWidth() + settings.marginX;
		var posY = elemPos.top + $elem.outerHeight() + settings.marginY;
		
		//adjust to ensure tip is inside viewable screen
		var right = posX + $tip.outerWidth();
		var bottom = posY + $tip.outerHeight();
		
		var windowWidth = $(window).width() + $(window).scrollLeft()-5;
		var windowHeight = $(window).height() + $(window).scrollTop()-5;
		
		posX = (right > windowWidth) ? posX - (right - windowWidth) : posX;
		posY = (bottom > windowHeight) ? posY - (bottom - windowHeight) : posY

		$tip.css({ left: posX, top: posY });
	}
})(jQuery);
Let me know what you think.
Last edited by pickle on Tue Apr 06, 2010 12:16 pm, edited 6 times in total.
Reason: Changing from [code] to [syntax]
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Re: "Tipped": jQuery tooltip plugin

Post by s.dot »

Hey, I have been looking for something like this! I found all of the other tooltip plugins not to my liking, with only one that was suitable. Do you have a working demo somewhere?
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: "Tipped": jQuery tooltip plugin

Post by pickle »

Not really - but I can show examples here if you want. It's pretty trivial to implement if you want to try it yourself.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Re: "Tipped": jQuery tooltip plugin

Post by s.dot »

Loving this, pickle. :)

I am implementing it on a forum board to give a "topic preview" when the topic title is hovered over. A couple of Q's..

1) When I hover the topic title, the tipped div shows as expected, but the title attribute pops up like a normal browser title hover. It's the same content, so it looks kind of silly having the tipped and the browser show the same title. I'd like to get rid of the browser popping up the title information. Possible?

2) Positioning. How does it determine where to pop up at, currently? Is it movable. I tried setting the top/left positioning with no success.

Here's the html I'm using it on:

Code: Select all

<a class="ftopic" href="http://localhost/site/forums.php?t=1" title="And I am in the general chat forum.  and i cover multiple lines  do you  ..">I am a general topic</a>
here's the css:

Code: Select all

#tipped{ 
  position:absolute;
  display:none;
  background-color: #fff;
  border: solid 1px #e6e6e6;
  padding: 3px;
}
#tipped-closer-wrapper{ /* This is the id given to the <div/> around close button */ }
#tipped-closer{ /* comment */ }
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: "Tipped": jQuery tooltip plugin

Post by pickle »

1) Go to the project page on Google code: http://code.google.com/p/tipped/ to download the latest version. The 'title' problem is fixed there.
2) The tip appears at the bottom right corner of the triggering element. From that point, it is offset by the marginX and marginY options, which default to 10 pixels. This being done in Javascript, any positioning you apply in CSS will be ignored.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Re: "Tipped": jQuery tooltip plugin

Post by s.dot »

1) I have jquery.tipped-1.4.js, which appears to be the latest one? (Maybe I'm missing it, your initial post says v2 is finished)
2) Perhaps a function or argument could be written to offset the position? Dunno how tricky this would be. I could edit the position myself (download full version and edit and then minify it) but this is the coding critique forum. :-D
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: "Tipped": jQuery tooltip plugin

Post by pickle »

1) What are you hovering over? Is there a URL I can look at? Are you using jQuery 1.4.2 (not sure how older versions would break it)?
2) How would you imagine this function working? Similar to the param option in that it accepts the triggering element, and returns absolute x & y co-ordinates?
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Re: "Tipped": jQuery tooltip plugin

Post by s.dot »

1) PM'd you
2) I imagine just some param/function to set the absolute values of the position. Perhaps $(el).tipped({ xpos: x, ypos: y})? Or maybe some options to set to top/left top/middle center/center etc. Not sure how hard this would be with your ensuring the tip is fully on screen. Or maybe popup where the mouseover occurs (this would be ideal).
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Re: "Tipped": jQuery tooltip plugin

Post by s.dot »

OK, noticed a couple of more things. Don't mean to harass you, really :D Just stating suggestions for how I would like to use it (perhaps someone else would like to use it the same way).

CSS width property for #tipped does not work, but max-width does.
Should width work?

How about a delay setting for the hover mode? Eg hover for x milliseconds before tipped pops up. For my use, it would be useful to see if the user is wanting to click the topic or waiting to read the preview. If they're just wanting to click, no need to show the preview. But if they hover, then I can give them what they want.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: "Tipped": jQuery tooltip plugin

Post by pickle »

The width gets reset every time the tip is shown. This is because only one DOM element is used for every tip. The width needs to be reset so the tip is only as wide as is necessary.

The delay is simple to add. I'll do so and roll out v1.5.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: "Tipped": jQuery tooltip plugin

Post by pickle »

1.5 has been rolled out: http://code.google.com/p/tipped/
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
Post Reply