Phillip
Phillip

Reputation: 1570

jQuery: Attach Tooltip to Mouse Cursor Position Using Plugin Tipsy

I've been using an old jQuery plugin called Tipsy. I like the way the tooltips look and for the most part I like the plugin. Unfortunately it only shows tooltips relative to the element it is retrieving the tooltip from. I'd like to show the tooltip in the position of the mouse cursor instead. Is there any way to do this? I would personally like to use this tooltip plugin if at all possible.

I have tried playing with the plugin for a little while, but the code is beyond my current knowledge of jQuery. I thought trying to capture the event when firing the 'show' function would let me get the coordinates where the event was fired but that didn't work. Any help would be greatly appreciated. I'll show the source JS file below:

// tipsy, facebook style tooltips for jquery
// version 1.0.0a
// (c) 2008-2010 jason frame [[email protected]]
// releated under the MIT license

(function($) {

function fixTitle($ele) {
    if ($ele.attr('title') || typeof($ele.attr('original-title')) != 'string') {
        $ele.attr('original-title', $ele.attr('title') || '').removeAttr('title');
    }
}

function Tipsy(element, options) {
    this.$element = $(element);
    this.options = options;
    this.enabled = true;
    fixTitle(this.$element);
}

Tipsy.prototype = {
    show: function() {
        var title = this.getTitle();
        if (title && this.enabled) {
            var $tip = this.tip();

            $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
            $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
            $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);

            var pos = $.extend({}, this.$element.offset(), {
                width: this.$element[0].offsetWidth,
                height: this.$element[0].offsetHeight
            });

            var actualWidth = $tip[0].offsetWidth, actualHeight = $tip[0].offsetHeight;
            var gravity = (typeof this.options.gravity == 'function')
                            ? this.options.gravity.call(this.$element[0])
                            : this.options.gravity;

            var tp;
            switch (gravity.charAt(0)) {
                case 'n':
                    tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
                    break;
                case 's':
                    tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
                    break;
                case 'e':
                    tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
                    break;
                case 'w':
                    tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
                    break;
            }

            if (gravity.length == 2) {
                if (gravity.charAt(1) == 'w') {
                    tp.left = pos.left + pos.width / 2 - 15;
                } else {
                    tp.left = pos.left + pos.width / 2 - actualWidth + 15;
                }
            }

            $tip.css(tp).addClass('tipsy-' + gravity);

            if (this.options.fade) {
                $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
            } else {
                $tip.css({visibility: 'visible', opacity: this.options.opacity});
            }
        }
    },

    hide: function() {
        if (this.options.fade) {
            this.tip().stop().fadeOut(function() { $(this).remove(); });
        } else {
            this.tip().remove();
        }
    },

    getTitle: function() {
        var title, $e = this.$element, o = this.options;
        fixTitle($e);
        var title, o = this.options;
        if (typeof o.title == 'string') {
            title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
        } else if (typeof o.title == 'function') {
            title = o.title.call($e[0]);
        }
        title = ('' + title).replace(/(^\s*|\s*$)/, "");
        return title || o.fallback;
    },

    tip: function() {
        if (!this.$tip) {
            this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"/></div>');
        }
        return this.$tip;
    },

    validate: function() {
        if (!this.$element[0].parentNode) {
            this.hide();
            this.$element = null;
            this.options = null;
        }
    },

    enable: function() { this.enabled = true; },
    disable: function() { this.enabled = false; },
    toggleEnabled: function() { this.enabled = !this.enabled; }
};

$.fn.tipsy = function(options) {

    if (options === true) {
        return this.data('tipsy');
    } else if (typeof options == 'string') {
        return this.data('tipsy')[options]();
    }

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

    function get(ele) {
        var tipsy = $.data(ele, 'tipsy');
        if (!tipsy) {
            tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
            $.data(ele, 'tipsy', tipsy);
        }
        return tipsy;
    }

    function enter() {
        var tipsy = get(this);
        tipsy.hoverState = 'in';
        if (options.delayIn == 0) {
            tipsy.show();
        } else {
            setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
        }
    };

    function leave() {
        var tipsy = get(this);
        tipsy.hoverState = 'out';
        if (options.delayOut == 0) {
            tipsy.hide();
        } else {
            setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
        }
    };

    if (!options.live) this.each(function() { get(this); });

    if (options.trigger != 'manual') {
        var binder   = options.live ? 'live' : 'bind',
            eventIn  = options.trigger == 'hover' ? 'mouseenter' : 'focus',
            eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
        this[binder](eventIn, enter)[binder](eventOut, leave);
    }

    return this;

};

$.fn.tipsy.defaults = {
    delayIn: 0,
    delayOut: 0,
    fade: false,
    fallback: '',
    gravity: 'n',
    html: false,
    live: false,
    offset: 0,
    opacity: 0.8,
    title: 'title',
    trigger: 'hover'
};

// Overwrite this method to provide options on a per-element basis.
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
// (remember - do not modify 'options' in place!)
$.fn.tipsy.elementOptions = function(ele, options) {
    return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
};

$.fn.tipsy.autoNS = function() {
    return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
};

$.fn.tipsy.autoWE = function() {
    return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
};

})(jQuery);

Upvotes: 3

Views: 1647

Answers (1)

davidkonrad
davidkonrad

Reputation: 85588

I did not knew tipsy before, and could not find a place where a full package was downloadable. So took your code and found the CSS from the website, along with the arrow glyph to produce this demo -> http://jsfiddle.net/df108obx/

I have made //ANSWER comments anywhere some code is added. I thought the best approach was to create a new gravity style "x", that places the tooltip with base from where the mouse was, when the tipsy is activated. So

  1. Added a mouse : {x: 0, y: 0} object to default options
  2. Now storing (e) mouse position in the enter() function
  3. Added a case 'x' : in the gravity switch in the show() function

There is certainly room for improvement, but this may be a good skeleton for extending tipsy with new ways of showing itself.

Upvotes: 1

Related Questions