/* * 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
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 = $('

') // 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("
"); 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);