Hi, I made a small adjustment to the jQuery Tooltip plugin from bassistance. The mouseover event is now passed to the bodyhandler, this makes it easy to use event delegation of bubbling. You an for example attach a tooltip to a TR instead of adding it to all TDs in the TR.
The old way: $('#mytable > tr.myclass > td').tooltip({ bodyHandler: function() { var id = $(this).attr("id"); return CreateTooltip(id); }, track: true }); The new way: $('#mytable > tr.myclass').tooltip({ bodyHandler: function(e) { var id = $(e.target).attr("id"); return CreateTooltip(id); }, track: true }); Maybe Jörn Zaefferer can add this to a next release of the tooltip. Regards, Jochen The code: /* * jQuery Tooltip plugin 1.3 * * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/ * http://docs.jquery.com/Plugins/Tooltip * * Copyright (c) 2006 - 2008 Jörn Zaefferer * * $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $ * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Changes by Jochen Jonckheere (c) 2009 * - Passing the mouseover event to the bodyhandler */ ; (function($) { // the tooltip element var helper = {}, // the current tooltipped element current, // the title of the current element, used for restoring title, // timeout id for delayed tooltips tID, // IE 5.5 or 6 IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent), // flag for mouse tracking track = false; $.tooltip = { blocked: false, defaults: { delay: 200, fade: false, showURL: true, extraClass: "", top: 15, left: 15, id: "tooltip" }, block: function() { $.tooltip.blocked = !$.tooltip.blocked; } }; $.fn.extend({ tooltip: function(settings) { settings = $.extend({}, $.tooltip.defaults, settings); createHelper(settings); return this.each(function() { $.data(this, "tooltip", settings); this.tOpacity = helper.parent.css("opacity"); // copy tooltip into its own expando and remove the title this.tooltipText = this.title; $(this).removeAttr("title"); // also remove alt attribute to prevent default tooltip in IE this.alt = ""; }) .mouseover(save) .mouseout(hide) .click(hide); }, fixPNG: IE ? function() { return this.each(function() { var image = $(this).css('backgroundImage'); if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) { image = RegExp.$1; $(this).css({ 'backgroundImage': 'none', 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')" }).each(function() { var position = $(this).css('position'); if (position != 'absolute' && position != 'relative') $(this).css('position', 'relative'); }); } }); } : function() { return this; }, unfixPNG: IE ? function() { return this.each(function() { $(this).css({ 'filter': '', backgroundImage: '' }); }); } : function() { return this; }, hideWhenEmpty: function() { return this.each(function() { $(this)[$(this).html() ? "show" : "hide"](); }); }, url: function() { return this.attr('href') || this.attr('src'); } }); function createHelper(settings) { // there can be only one tooltip helper if (helper.parent) return; // create the helper, h3 for title, div for url helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>') // add to document .appendTo(document.body) // hide it at first .hide(); // apply bgiframe if available if ($.fn.bgiframe) helper.parent.bgiframe(); // save references to title and url elements helper.title = $('h3', helper.parent); helper.body = $('div.body', helper.parent); helper.url = $('div.url', helper.parent); } function settings(element) { return $.data(element, "tooltip"); } // main event handler to start showing tooltips function handle(event) { // show helper, either with timeout or on instant if (settings(this).delay) tID = setTimeout(show, settings(this).delay); else show(); // if selected, update the helper position when the mouse moves track = !!settings(this).track; $(document.body).bind('mousemove', update); // update at least once update(event); } // save elements title before the tooltip is displayed function save(event) { // if this is the current source, or it has no title (occurs with click event), stop if ($.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler)) return; // save current current = this; title = this.tooltipText; if (settings(this).bodyHandler) { helper.title.hide(); var bodyContent = settings(this).bodyHandler.call(this, event); if (bodyContent.nodeType || bodyContent.jquery) { helper.body.empty().append(bodyContent) } else { helper.body.html(bodyContent); } helper.body.show(); } else if (settings(this).showBody) { var parts = title.split(settings(this).showBody); helper.title.html(parts.shift()).show(); helper.body.empty(); for (var i = 0, part; (part = parts[i]); i++) { if (i > 0) helper.body.append("<br/>"); helper.body.append(part); } helper.body.hideWhenEmpty(); } else { helper.title.html(title).show(); helper.body.hide(); } // if element has href or src, add and show it, otherwise hide it if (settings(this).showURL && $(this).url()) helper.url.html($(this).url().replace('http://', '')).show (); else helper.url.hide(); // add an optional class for this tip helper.parent.addClass(settings(this).extraClass); // fix PNG background for IE if (settings(this).fixPNG) helper.parent.fixPNG(); handle.apply(this, arguments); } // delete timeout and show helper function show() { tID = null; if ((!IE || !$.fn.bgiframe) && settings(current).fade) { if (helper.parent.is(":animated")) helper.parent.stop().show().fadeTo(settings (current).fade, current.tOpacity); else helper.parent.is(':visible') ? helper.parent.fadeTo (settings(current).fade, current.tOpacity) : helper.parent.fadeIn (settings(current).fade); } else { helper.parent.show(); } update(); } /** * callback for mousemove * updates the helper position * removes itself when no current element */ function update(event) { if ($.tooltip.blocked) return; if (event && event.target.tagName == "OPTION") { return; } // stop updating when tracking is disabled and the tooltip is visible if (!track && helper.parent.is(":visible")) { $(document.body).unbind('mousemove', update) } // if no current element is available, remove this listener if (current == null) { $(document.body).unbind('mousemove', update); return; } // remove position helper classes helper.parent.removeClass("viewport-right").removeClass ("viewport-bottom"); var left = helper.parent[0].offsetLeft; var top = helper.parent[0].offsetTop; if (event) { // position the helper 15 pixel to bottom right, starting from mouse position left = event.pageX + settings(current).left; top = event.pageY + settings(current).top; var right = 'auto'; if (settings(current).positionLeft) { right = $(window).width() - left; left = 'auto'; } helper.parent.css({ left: left, right: right, top: top }); } var v = viewport(), h = helper.parent[0]; // check horizontal position if (v.x + v.cx < h.offsetLeft + h.offsetWidth) { left -= h.offsetWidth + 20 + settings(current).left; helper.parent.css({ left: left + 'px' }).addClass ("viewport-right"); } // check vertical position if (v.y + v.cy < h.offsetTop + h.offsetHeight) { top -= h.offsetHeight + 20 + settings(current).top; helper.parent.css({ top: top + 'px' }).addClass("viewport- bottom"); } } function viewport() { return { x: $(window).scrollLeft(), y: $(window).scrollTop(), cx: $(window).width(), cy: $(window).height() }; } // hide helper and restore added classes and the title function hide(event) { if ($.tooltip.blocked) return; // clear timeout if possible if (tID) clearTimeout(tID); // no more current element current = null; var tsettings = settings(this); function complete() { helper.parent.removeClass(tsettings.extraClass).hide().css ("opacity", ""); } if ((!IE || !$.fn.bgiframe) && tsettings.fade) { if (helper.parent.is(':animated')) helper.parent.stop().fadeTo(tsettings.fade, 0, complete); else helper.parent.stop().fadeOut(tsettings.fade, complete); } else complete(); if (settings(this).fixPNG) helper.parent.unfixPNG(); } })(jQuery);