Jump To …

hover.js

(function ($) {

  $.Hover = function () {
    this._delay = $.Hover.delay;
    this._distance = $.Hover.distance;
    this._leave = $.Hover.leave
  };

  $.extend($.Hover, {

    delay: 100,

    distance: 10,
    leave: 0
  })


  $.extend($.Hover.prototype, {

    delay: function (delay) {
      this._delay = delay;
      return this;
    },

    distance: function (distance) {
      this._distance = distance;
      return this;
    },

    leave: function (leave) {
      this._leave = leave;
      return this;
    }
  })
  var event = $.event,
    handle = event.handle,
    onmouseenter = function (ev) {

now start checking mousemoves to update location

      var delegate = ev.delegateTarget || ev.currentTarget;
      var selector = ev.handleObj.selector;
      var pending = $.data(delegate, "_hover" + selector);

prevents another mouseenter until current has run its course

      if (pending) {

Under some circumstances, mouseleave may never fire (e.g., the element is removed while hovered) so if we've entered another element, wait the leave time, then force it to release.

        if (!pending.forcing) {
          pending.forcing = true;
          clearTimeout(pending.leaveTimer);
          var leaveTime = pending.leaving ? Math.max(0, pending.hover.leave - (new Date() - pending.leaving)) : pending.hover.leave;
          var self = this;

          setTimeout(function () {
            pending.callHoverLeave();
            onmouseenter.call(self, ev);
          }, leaveTime);
        }
        return;
      }
      var loc = {
        pageX: ev.pageX,
        pageY: ev.pageY
      },

The current distance

        dist = 0,

Timer that checks for the distance travelled

        timer, enteredEl = this,

If we are hovered

        hovered = false,

The previous event

        lastEv = ev,

The $.Hover instance passed to events

        hover = new $.Hover(),

timer if hover.leave has been called

        leaveTimer,

Callback for triggering hoverleave

        callHoverLeave = function () {
          $.each(event.find(delegate, ["hoverleave"], selector), function () {
            this.call(enteredEl, ev, hover)
          })
          cleanUp();
        },
        mousemove = function (ev) {
          clearTimeout(leaveTimer);

Update the distance and location

          dist += Math.pow(ev.pageX - loc.pageX, 2) + Math.pow(ev.pageY - loc.pageY, 2);
          loc = {
            pageX: ev.pageX,
            pageY: ev.pageY
          }
          lastEv = ev
        },
        mouseleave = function (ev) {
          clearTimeout(timer);
          if (hovered) {

go right away

            if (hover._leave === 0) {
              callHoverLeave();
            } else {
              clearTimeout(leaveTimer);

leave the hover after the time set in hover.leave(time)

              pending.leaving = new Date();
              leaveTimer = pending.leaveTimer = setTimeout(function () {
                callHoverLeave();
              }, hover._leave)
            }
          } else {
            cleanUp();
          }
        },
        cleanUp = function () {

Unbind all events and data

          $(enteredEl).unbind("mouseleave", mouseleave)
          $(enteredEl).unbind("mousemove", mousemove);
          $.removeData(delegate, "_hover" + selector)
        },
        hoverenter = function () {
          $.each(event.find(delegate, ["hoverenter"], selector), function () {
            this.call(enteredEl, lastEv, hover)
          })
          hovered = true;
        };
      pending = {
        callHoverLeave: callHoverLeave,
        hover: hover
      };
      $.data(delegate, "_hover" + selector, pending);

Bind the mousemove event

      $(enteredEl).bind("mousemove", mousemove).bind("mouseleave", mouseleave);

call hoverinit for each element with the hover instance

      $.each(event.find(delegate, ["hoverinit"], selector), function () {
        this.call(enteredEl, ev, hover)
      })

      if (hover._delay === 0) {
        hoverenter();
      } else {
        timer = setTimeout(function () {

check that we aren't moving around

          if (dist < hover._distance && $(enteredEl).queue().length == 0) {
            hoverenter();
            return;
          } else {

Reset distance and timer

            dist = 0;
            timer = setTimeout(arguments.callee, hover._delay)
          }
        }, hover._delay);
      }
    };

Attach events

  event.setupHelper([

  "hoverinit",

  "hoverenter",

  "hoverleave",

  "hovermove"], "mouseenter", onmouseenter)

  return $;
})(jQuery);