Skip to content
This repository has been archived by the owner on Jun 14, 2020. It is now read-only.

Commit

Permalink
Use event delegation for common events handlers. Add new data-qtip-id…
Browse files Browse the repository at this point in the history
… attribute to target and tooltips. Remove data-hasqtip helper in favour of former improvement.
  • Loading branch information
Craga89 committed Apr 26, 2013
1 parent 8e06fe7 commit 4acc8d0
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 76 deletions.
2 changes: 1 addition & 1 deletion src/core/button.js
Expand Up @@ -31,7 +31,7 @@ PROTOTYPE._createButton = function()
elements.button.appendTo(elements.titlebar || tooltip)
.attr('role', 'button')
.click(function(event) {
if(!tooltip.hasClass(disabledClass)) { self.hide(event); }
if(!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); }
return FALSE;
});
};
Expand Down
17 changes: 12 additions & 5 deletions src/core/class.js
Expand Up @@ -20,7 +20,7 @@ function QTip(target, options, id, attr) {
target: $(),
disabled: FALSE,
attr: attr,
onTarget: FALSE,
onTooltip: FALSE,
lastClass: ''
};

Expand Down Expand Up @@ -50,7 +50,7 @@ PROTOTYPE.render = function(show) {
// Create tooltip element
this.tooltip = elements.tooltip = tooltip = $('<div/>', {
'id': this._id,
'class': [ NAMESPACE, defaultClass, options.style.classes, NAMESPACE + '-pos-' + options.position.my.abbrev() ].join(' '),
'class': [ NAMESPACE, CLASS_DEFAULT, options.style.classes, NAMESPACE + '-pos-' + options.position.my.abbrev() ].join(' '),
'width': options.style.width || '',
'height': options.style.height || '',
'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,
Expand All @@ -62,7 +62,8 @@ PROTOTYPE.render = function(show) {
'aria-describedby': this._id + '-content',
'aria-hidden': TRUE
})
.toggleClass(disabledClass, this.disabled)
.toggleClass(CLASS_DISABLED, this.disabled)
.attr(ATTR_ID, this.id)
.data(NAMESPACE, this)
.appendTo(posOptions.container)
.append(
Expand Down Expand Up @@ -134,6 +135,9 @@ PROTOTYPE.render = function(show) {
self.hiddenDuringWait = FALSE;
});

// Expose API
QTIP.api[this.id] = this;

return this;
};

Expand Down Expand Up @@ -165,7 +169,7 @@ PROTOTYPE.destroy = function(immediate) {
this._unassignEvents();

// Remove api object and ARIA attributes
target.removeData(NAMESPACE).removeAttr(HASATTR)
target.removeData(NAMESPACE).removeAttr(ATTR_ID)
.removeAttr('aria-describedby');

// Reset old title attribute if removed
Expand All @@ -179,7 +183,10 @@ PROTOTYPE.destroy = function(immediate) {
// Remove ID from used id objects, and delete object references
// for better garbage collection and leak protection
this.options = this.elements = this.cache = this.timers =
this.plugins = this.mouse = usedIDs[this.id] = NULL;
this.plugins = this.mouse = NULL;

// Delete epoxsed API object
delete QTIP.api[this.id];
}

// If an immediate destory is needed
Expand Down
17 changes: 10 additions & 7 deletions src/core/constants.js
Expand Up @@ -24,14 +24,17 @@ SHIFT = 'shift',
QTIP, PROTOTYPE, CORNER, CHECKS,
PLUGINS = {},
NAMESPACE = 'qtip',
HASATTR = 'data-hasqtip',
ATTR_HAS = 'data-hasqtip',
ATTR_ID = 'data-qtip-id',
WIDGET = ['ui-widget', 'ui-tooltip'],
usedIDs = {},
selector = 'div.'+NAMESPACE,
defaultClass = NAMESPACE + '-default',
focusClass = NAMESPACE + '-focus',
hoverClass = NAMESPACE + '-hover',
disabledClass = 'qtip-disabled',
SELECTOR = '.'+NAMESPACE,

CLASS_FIXED = NAMESPACE+'-fixed',
CLASS_DEFAULT = NAMESPACE + '-default',
CLASS_FOCUS = NAMESPACE + '-focus',
CLASS_HOVER = NAMESPACE + '-hover',
CLASS_DISABLED = NAMESPACE+'-disabled',

replaceSuffix = '_replacedByqTip',
oldtitle = 'oldtitle',
trackingBound;
Expand Down
4 changes: 2 additions & 2 deletions src/core/disable.js
Expand Up @@ -2,11 +2,11 @@ PROTOTYPE.disable = function(state) {
if(this.destroyed) { return this; }

if('boolean' !== typeof state) {
state = !(this.tooltip.hasClass(disabledClass) || this.disabled);
state = !(this.tooltip.hasClass(CLASS_DISABLED) || this.disabled);
}

if(this.rendered) {
this.tooltip.toggleClass(disabledClass, state)
this.tooltip.toggleClass(CLASS_DISABLED, state)
.attr('aria-disabled', state);
}

Expand Down
93 changes: 58 additions & 35 deletions src/core/events.js
@@ -1,5 +1,5 @@
function showMethod(event) {
if(this.tooltip.hasClass(disabledClass)) { return FALSE; }
if(this.tooltip.hasClass(CLASS_DISABLED)) { return FALSE; }

// Clear hide timers
clearTimeout(this.timers.show);
Expand All @@ -14,11 +14,11 @@ function showMethod(event) {
}

function hideMethod(event) {
if(this.tooltip.hasClass(disabledClass)) { return FALSE; }
if(this.tooltip.hasClass(CLASS_DISABLED)) { return FALSE; }

// Check if new target was actually the tooltip element
var relatedTarget = $(event.relatedTarget),
ontoTooltip = relatedTarget.closest(selector)[0] === this.tooltip[0],
ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0],
ontoTarget = relatedTarget[0] === this.options.show.target[0];

// Clear timers and stop animation queue
Expand Down Expand Up @@ -50,7 +50,7 @@ function hideMethod(event) {
}

function inactiveMethod(event) {
if(this.tooltip.hasClass(disabledClass)) { return FALSE; }
if(this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return FALSE; }

// Clear timer
clearTimeout(this.timers.inactive);
Expand Down Expand Up @@ -86,6 +86,51 @@ PROTOTYPE._unbind = function(targets, suffix) {
$(targets).unbind('.' + this._id + (suffix ? '-'+suffix : ''));
};

// Apply common event handlers using delegate (avoids excessive .bind calls!)
var ns = '.'+NAMESPACE;
function delegate(selector, events, method) {
$(document.body).delegate(selector,
(events.split ? events : events.join(ns + ' ')) + ns,
function() {
var api = QTIP.api[ $.attr(this, ATTR_ID) ];
api && method.apply(api, arguments);
}
);
}

$(function() {
delegate(SELECTOR, ['mouseenter', 'mouseleave'], function(event) {
var state = event.type === 'mouseenter',
tooltip = $(event.currentTarget),
target = $(event.relatedTarget || event.target),
options = this.options;

// On mouseenter...
if(state) {
// Focus the tooltip on mouseenter (z-index stacking)
this.focus(event);

// Clear hide timer on tooltip hover to prevent it from closing
tooltip.hasClass(CLASS_FIXED) && !tooltip.hasClass(CLASS_DISABLED) && clearTimeout(this.timers.hide);
}

// On mouseleave...
else {
// Hide when we leave the tooltip and not onto the show target (if a hide event is set)
if(options.position.target === 'mouse' && options.hide.event &&
options.show.target && !target.closest(options.show.target[0]).length) {
this.hide(event);
}
}

// Add hover class
tooltip.toggleClass(CLASS_HOVER, state);
});

// Define events which reset the 'inactive' event handler
delegate('['+ATTR_ID+']', QTIP.inactiveEvents, inactiveMethod);
});

// Event trigger
PROTOTYPE._trigger = function(type, args, event) {
var callback = $.Event('tooltip'+type);
Expand Down Expand Up @@ -116,17 +161,6 @@ PROTOTYPE._assignEvents = function() {
hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [],
toggleEvents = [];

// Focus/blur
this._bind(tooltip, ['mouseenter', 'mouseleave'], function(event) {
var state = event.type === 'mouseenter';

// Focus the tooltip on mouseenter (z-index stacking)
if(state) { this.focus(event); }

// Add hover class
tooltip.toggleClass(hoverClass, state);
});

// Hide tooltips when leaving current window/frame (but not select/option elements)
if(/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') {
this._bind(documentTarget, ['mouseout', 'blur'], function(event) {
Expand All @@ -136,15 +170,9 @@ PROTOTYPE._assignEvents = function() {
});
}

// Enable hide.fixed
// Enable hide.fixed by adding appropriate class
if(options.hide.fixed) {
// Add tooltip as a hide target
hideTarget = hideTarget.add(tooltip);

// Clear hide timer on tooltip hover to prevent it from closing
this._bind(tooltip, 'mouseover', function() {
!this.tooltip.hasClass(disabledClass) && clearTimeout(this.timers.hide);
});
hideTarget = hideTarget.add( tooltip.addClass(CLASS_FIXED) );
}

/*
Expand All @@ -161,8 +189,8 @@ PROTOTYPE._assignEvents = function() {
if(('' + options.hide.event).indexOf('unfocus') > -1) {
this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function(event) {
var elem = $(event.target),
enabled = this.rendered && !this.tooltip.hasClass(disabledClass) && this.tooltip[0].offsetWidth > 0,
isAncestor = elem.parents(selector).filter(this.tooltip[0]).length > 0;
enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0,
isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0;

if(elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor &&
!this.target.has(elem[0]).length && enabled
Expand All @@ -175,7 +203,7 @@ PROTOTYPE._assignEvents = function() {
// Check if the tooltip hides when inactive
if('number' === typeof options.hide.inactive) {
// Bind inactive method to show target(s) as a custom event
showTarget.bind('qtip-'+this.id+'-inactive', $.proxy(inactiveMethod, this));
this._bind(showTarget, 'qtip-'+this.id+'-inactive', inactiveMethod);

// Define events which reset the 'inactive' event handler
this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod, '-inactive');
Expand Down Expand Up @@ -211,19 +239,14 @@ PROTOTYPE._assignEvents = function() {
if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
this.hide(event);
}

// Cache mousemove coords on show targets
this._storeMouse(event);
});
}

// Mouse positioning events
if(posOptions.target === 'mouse') {
// Cache mousemove coords on show targets
this._bind(showTarget.add(tooltip), 'mousemove', this._storeMouse);

// Hide when we leave the tooltip and not onto the show target
options.hide.event && this._bind(tooltip, 'mouseleave', function(event) {
!$(event.relatedTarget || event.target).closest(showTarget[0]).length && this.hide(event);
});

// If mouse adjustment is on...
if(posOptions.adjust.mouse) {
// Apply a mouseleave event so we don't get problems with overlapping
Expand All @@ -237,7 +260,7 @@ PROTOTYPE._assignEvents = function() {
// Update tooltip position on mousemove
this._bind(documentTarget, 'mousemove', function(event) {
// Update the tooltip position only if the tooltip is visible and adjustment is enabled
if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(disabledClass) && this.tooltip[0].offsetWidth > 0) {
if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) {
this.reposition(event || this.mouse);
}
});
Expand Down
10 changes: 5 additions & 5 deletions src/core/focus.js
@@ -1,14 +1,14 @@
PROTOTYPE.focus = function(event) {
if(!this.rendered || this.destroyed) { return this; }

var qtips = $(selector),
var qtips = $(SELECTOR),
tooltip = this.tooltip,
curIndex = parseInt(tooltip[0].style.zIndex, 10),
newIndex = QTIP.zindex + qtips.length,
focusedElem;

// Only update the z-index if it has changed and tooltip is not already focused
if(!tooltip.hasClass(focusClass)) {
if(!tooltip.hasClass(CLASS_FOCUS)) {
// tooltipfocus event
if(this._trigger('focus', [newIndex], event)) {
// Only update z-index's if they've changed
Expand All @@ -21,11 +21,11 @@ PROTOTYPE.focus = function(event) {
});

// Fire blur event for focused tooltip
qtips.filter('.' + focusClass).qtip('blur', event);
qtips.filter('.' + CLASS_FOCUS).qtip('blur', event);
}

// Set the new z-index
tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
}
}

Expand All @@ -36,7 +36,7 @@ PROTOTYPE.blur = function(event) {
if(!this.rendered || this.destroyed) { return this; }

// Set focused status to FALSE
this.tooltip.removeClass(focusClass);
this.tooltip.removeClass(CLASS_FOCUS);

// tooltipblur event
this._trigger('blur', [ this.tooltip.css('zIndex') ], event);
Expand Down
10 changes: 7 additions & 3 deletions src/core/jquery_methods.js
Expand Up @@ -67,7 +67,7 @@ function init(elem, id, opts)
}

// Add has-qtip attribute
elem.attr(HASATTR, true);
elem.attr(ATTR_HAS, id);

// Remove title attribute and store it if present
if(config.suppress && (title = elem.attr('title'))) {
Expand Down Expand Up @@ -150,14 +150,15 @@ QTIP.bind = function(opts, event)

// Find next available ID, or use custom ID if provided
id = $.isArray(opts.id) ? opts.id[i] : opts.id;
id = !id || id === FALSE || id.length < 1 || usedIDs[id] ? QTIP.nextid++ : (usedIDs[id] = id);
id = !id || id === FALSE || id.length < 1 || QTIP.api[id] ? QTIP.nextid++ : id;

// Setup events namespace
namespace = '.qtip-'+id+'-create';

// Initialize the qTip and re-grab newly sanitized options
api = init($(this), id, opts);
if(api === FALSE) { return TRUE; }
else { QTIP.api[id] = api; }
options = api.options;

// Initialize plugins
Expand Down Expand Up @@ -225,4 +226,7 @@ QTIP.bind = function(opts, event)
// Prerendering is enabled, create tooltip now
if(options.show.ready || options.prerender) { hoverIntent(event); }
});
};
};

// Populated in render method
QTIP.api = {};
2 changes: 1 addition & 1 deletion src/core/jquery_overrides.js
Expand Up @@ -61,7 +61,7 @@ $.each({
if(!$.ui) {
$['cleanData'+replaceSuffix] = $.cleanData;
$.cleanData = function( elems ) {
for(var i = 0, elem; (elem = $( elems[i] )).length && elem.attr(HASATTR); i++) {
for(var i = 0, elem; (elem = $( elems[i] )).length && elem.attr(ATTR_ID); i++) {
try { elem.triggerHandler('removeqtip'); }
catch( e ) {}
}
Expand Down
8 changes: 3 additions & 5 deletions src/core/options.js
Expand Up @@ -151,10 +151,8 @@ CHECKS = PROTOTYPE.checks = {
},

// Style checks
'^style.classes$': function(obj, o, v) {
this.tooltip.attr('class',
[ NAMESPACE, defaultClass, v, NAMESPACE + '-pos-' + this.options.position.my.abbrev() ].join(' ')
);
'^style.classes$': function(obj, o, v, p) {
this.tooltip.removeClass(p).addClass(v);
},
'^style.width|height': function(obj, o, v) {
this.tooltip.css(o, v);
Expand All @@ -163,7 +161,7 @@ CHECKS = PROTOTYPE.checks = {
this._setWidget();
},
'^style.def': function(obj, o, v) {
this.tooltip.toggleClass(defaultClass, !!v);
this.tooltip.toggleClass(CLASS_DEFAULT, !!v);
},

// Events check
Expand Down

0 comments on commit 4acc8d0

Please sign in to comment.