/* * Tooltip - jQuery plugin for styled tooltips * * Copyright (c) 2006 Jörn Zaefferer, Stefan Petre * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * */ /** * Display a customized tooltip instead of the default one * for every selected element. The tooltip behaviour mimics * the default one, but lets you style the tooltip and * specify the delay before displaying it. * * In addition, it displays the href value, if it is available. * * To style the tooltip, use these selectors in your stylesheet: * * #tooltip - The tooltip container * * #tooltip h3 - The tooltip title * * #tooltip p.body - The tooltip body, shown when using showBody * * #tooltip p.url - The tooltip url, shown when using showURL * * @example $('a, input, img').Tooltip(); * @desc Shows tooltips for anchors, inputs and images, if they have a title * * @example $('label').Tooltip({ * delay: 0, * track: true, * event: "click" * }); * @desc Shows tooltips for labels with no delay, tracking mousemovement, displaying the tooltip when the label is clicked. * * @example // modify global settings * $.extend($.fn.Tooltip.defaults, { * track: true, * delay: 0, * showURL: false, * showBody: " - ", * fixPNG: true * }); * // setup fancy tooltips * $('a.pretty').Tooltip({ * extraClass: "fancy" * }); $('img.pretty').Tooltip({ * extraClass: "fancy-img", * }); * @desc This example starts with modifying the global settings, applying them to all following Tooltips; Afterwards, Tooltips for anchors with class pretty are created with an extra class for the Tooltip: "fancy" for anchors, "fancy-img" for images * * @param Object settings (optional) Customize your Tooltips * @option Number delay The number of milliseconds before a tooltip is display, default is 250 * @option String event The event on which the tooltip is displayed, default is "mouseover", "click" works fine, too * @option Boolean track If true, let the tooltip track the mousemovement, default is false * @option Boolean showURL If true, shows the href or src attribute within p.url, default is true * @option String showBody If specified, uses the String to split the title, displaying the first part in the h3 tag, all following in the p.body tag, separated with <br/>s, default is null * @option String extraClass If specified, adds the class to the tooltip helper, default is null * @option Boolean fixPNG If true, fixes transparent PNGs in IE, default is false * * @name Tooltip * @type jQuery * @cat Plugins/Tooltip * @author Jörn Zaefferer (http://bassistance.de) */ (function($) { // the tooltip element var helper, // it's title part tTitle, // it's body part tBody, // it's url part tUrl, // the current tooltipped element current, // the title of the current element, used for restoring oldTitle, // timeout id for delayed tooltips tID; // the public plugin method $.fn.Tooltip = function(settings) { // setup configuration // TODO: allow multiple arguments to extend, see bug #344 settings = $.extend($.extend({}, arguments.callee.defaults), settings || {}); // there can be only one tooltip helper if( !helper ) { // create the helper, h3 for title, div for url helper = $('<div id="tooltip"><h3></h3><p class="body"></p><p class="url"></p></div>') // hide it at first .hide() // move to top and position absolute, to let it follow the mouse .css({ position: 'absolute', zIndex: 3000 }) // add to document .appendTo('body'); // save references to title and url elements tTitle = $('h3', helper); tBody = $('p:eq(0)', helper); tUrl = $('p:eq(1)', helper); } // bind events for every selected element with a title attribute $(this).filter('[@title]') // save settings into each element // TODO: pass settings via event system, not yet possible .each(function() { this.tSettings = settings; }) // bind events .bind("mouseover", save) .bind(settings.event, handle); return this; }; // main event handler to start showing tooltips function handle(event) { // show helper, either with timeout or on instant if( this.tSettings.delay ) tID = setTimeout(show, this.tSettings.delay); else show(); // if selected, update the helper position when the mouse moves if(this.tSettings.track) $('body').bind('mousemove', update); // update at least once update(event); // hide the helper when the mouse moves out of the element $(this).bind('mouseout', hide); } // save elements title before the tooltip is displayed function save() { // if this is the current source, or it has no title (occurs with click event), stop if(this == current || !this.title) return; // save current current = this; var source = $(this), settings = this.tSettings; // save title, remove from element and set to helper oldTitle = title = source.attr('title'); source.attr('title',''); if(settings.showBody) { var parts = title.split(settings.showBody); tTitle.html(parts.shift()); tBody.empty(); for(var i = 0, part; part = parts[i]; i++) { if(i > 0) tBody.append("<br/>"); tBody.append(part); } if(tBody.html()) tBody.show(); else tBody.hide(); } else { tTitle.html(title); tBody.hide(); } // if element has href or src, add and show it, otherwise hide it href = (source.attr('href') || source.attr('src')); if( settings.showURL && href ) tUrl.html(href.replace('http://', '')).show(); else tUrl.hide(); // add an optional class for this tip if( settings.extraClass ) { helper.addClass(settings.extraClass); } // fix PNG background for IE if (settings.fixPNG && $.browser.msie ) { helper.each(function () { if (this.currentStyle.backgroundImage != 'none') { var image = this.currentStyle.backgroundImage; image = image.substring(5, image.length - 2); $(this).css({ 'backgroundImage': 'none', 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')" }); } }); } } // delete timeout and show helper function show() { tID = null; helper.show(); update(); } /** * callback for mousemove * updates the helper position * removes itself when no current element */ function update(event) { // if no current element is available, remove this listener if( current == null ) { $('body').unbind('mousemove', update); return; } var left = helper[0].offsetLeft; var top = helper[0].offsetTop; if(event) { // get the current mouse position function pos(c) { var p = c == 'X' ? 'Left' : 'Top'; return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0; } // position the helper 15 pixel to bottom right, starting from mouse position left = pos('X') + 15; top = pos('Y') + 15; helper.css({ left: left + 'px', top: top + 'px' }); } var v = viewport(), h = helper[0]; // check horizontal position if(v.x + v.cx < h.offsetLeft + h.offsetWidth) { left -= h.offsetWidth + 20; helper.css({left: left + 'px'}); } // check vertical position if(v.y + v.cy < h.offsetTop + h.offsetHeight) { top -= h.offsetHeight + 20; helper.css({top: top + 'px'}); } } function viewport() { var e = document.documentElement || {}, b = document.body || {}, w = window; return { x: w.pageXOffset || e.scrollLeft || b.scrollLeft || 0, y: w.pageYOffset || e.scrollTop || b.scrollTop || 0, cx: min( e.clientWidth, b.clientWidth, w.innerWidth ), cy: min( e.clientHeight, b.clientHeight, w.innerHeight ) }; function min() { var v = Infinity; for( var i = 0; i < arguments.length; i++ ) { var n = arguments[i]; if( n && n < v ) v = n; } return v; } } // hide helper and restore added classes and the title function hide() { // clear timeout if possible if(tID) clearTimeout(tID); // no more current element current = null; helper.hide(); // remove optional class if( this.tSettings.extraClass ) { helper.removeClass( this.tSettings.extraClass); } // restore title and remove this listener $(this) .attr('title', oldTitle) .unbind('mouseout', hide); // remove PNG background fix for IE if( this.tSettings.fixPNG && $.browser.msie ) { helper.each(function () { $(this).css({'filter': '', backgroundImage: ''}); }); } } // define global defaults, editable by client $.fn.Tooltip.defaults = { delay: 250, event: "mouseover", track: false, showURL: true, showBody: null, extraClass: null, fixPNG: false }; })(jQuery);