feat(mouseenter/mouseleave): emulating ie events

This commit is contained in:
Misko Hevery 2012-02-10 21:16:50 -08:00
parent cae9ad4ba9
commit c8ee631c19
2 changed files with 115 additions and 39 deletions

View file

@ -467,6 +467,50 @@ forEach({
}; };
}); });
function createEventHandler(element) {
var eventHandler = function (event) {
if (!event.preventDefault) {
event.preventDefault = function() {
event.returnValue = false; //ie
};
}
if (!event.stopPropagation) {
event.stopPropagation = function() {
event.cancelBubble = true; //ie
};
}
if (!event.target) {
event.target = event.srcElement || document;
}
if (isUndefined(event.defaultPrevented)) {
var prevent = event.preventDefault;
event.preventDefault = function() {
event.defaultPrevented = true;
prevent.call(event);
};
event.defaultPrevented = false;
}
event.isDefaultPrevented = function() {
return event.defaultPrevented;
};
forEach(eventHandler.fns, function(fn){
fn.call(element, event);
});
// Remove monkey-patched methods (IE),
// as they would cause memory leaks in IE8.
// It shouldn't affect normal browsers, as their native methods are defined on prototype.
delete event.preventDefault;
delete event.stopPropagation;
delete event.isDefaultPrevented;
};
eventHandler.fns = [];
return eventHandler;
};
////////////////////////////////////////// //////////////////////////////////////////
// Functions iterating traversal. // Functions iterating traversal.
// These functions chain results into a single // These functions chain results into a single
@ -477,53 +521,41 @@ forEach({
dealoc: JQLiteDealoc, dealoc: JQLiteDealoc,
bind: function(element, type, fn){ bind: function bindFn(element, type, fn){
var bind = JQLiteData(element, 'bind'); var bind = JQLiteData(element, 'bind');
if (!bind) JQLiteData(element, 'bind', bind = {}); if (!bind) JQLiteData(element, 'bind', bind = {});
forEach(type.split(' '), function(type){ forEach(type.split(' '), function(type){
var eventHandler = bind[type]; var eventHandler = bind[type];
if (!eventHandler) { if (!eventHandler) {
bind[type] = eventHandler = function(event) { if (type == 'mouseenter' || type == 'mouseleave') {
if (!event.preventDefault) { var mouseenter = bind.mouseenter = createEventHandler(element);
event.preventDefault = function() { var mouseleave = bind.mouseleave = createEventHandler(element);
event.returnValue = false; //ie var counter = 0;
};
}
if (!event.stopPropagation) {
event.stopPropagation = function() {
event.cancelBubble = true; //ie
};
}
if (!event.target) {
event.target = event.srcElement || document;
}
if (isUndefined(event.defaultPrevented)) {
var prevent = event.preventDefault;
event.preventDefault = function() {
event.defaultPrevented = true;
prevent.call(event);
};
event.defaultPrevented = false;
}
event.isDefaultPrevented = function() { bindFn(element, 'mouseover', function(event) {
return event.defaultPrevented; counter++;
}; if (counter == 1) {
event.type = 'mouseenter';
forEach(eventHandler.fns, function(fn){ mouseenter(event);
fn.call(element, event); }
}); });
bindFn(element, 'mouseout', function(event) {
// Remove monkey-patched methods (IE), counter --;
// as they would cause memory leaks in IE8. if (counter == 0) {
// It shouldn't affect normal browsers, as their native methods are defined on prototype. event.type = 'mouseleave';
delete event.preventDefault mouseleave(event);
delete event.stopPropagation }
delete event.isDefaultPrevented });
}; eventHandler = bind[type];
eventHandler.fns = []; } else {
addEventListenerFn(element, type, eventHandler); eventHandler = bind[type] = createEventHandler(element);
addEventListenerFn(element, type, eventHandler);
}
} }
eventHandler.fns.push(fn); eventHandler.fns.push(fn);
}); });

View file

@ -603,6 +603,50 @@ describe('jqLite', function() {
browserTrigger(a, 'click'); browserTrigger(a, 'click');
}); });
describe('mouseenter-mouseleave', function() {
var root, parent, sibling, child, log;
beforeEach(function() {
log = '';
root = jqLite('<div>root<p>parent<span>child</span></p><ul></ul></div>');
parent = root.find('p');
sibling = root.find('ul');
child = parent.find('span');
parent.bind('mouseenter', function() { log += 'parentEnter;'; });
parent.bind('mouseleave', function() { log += 'parentLeave;'; });
parent.mouseover = function(event) { parent.data('bind').mouseover(event || {}); };
parent.mouseout = function(event) { parent.data('bind').mouseout(event || {}); };
child.bind('mouseenter', function() { log += 'childEnter;'; });
child.bind('mouseleave', function() { log += 'childLeave;'; });
child.mouseover = function(event) { child.data('bind').mouseover(event || {}); };
child.mouseout = function(event) { child.data('bind').mouseout(event || {}); };
});
afterEach(function() {
dealoc(root);
});
it('should fire mouseenter when coming from outside the browser window', function() {
if (window.jQuery) return;
parent.mouseover();
expect(log).toEqual('parentEnter;');
child.mouseover();
expect(log).toEqual('parentEnter;childEnter;');
child.mouseover();
expect(log).toEqual('parentEnter;childEnter;');
child.mouseout();
expect(log).toEqual('parentEnter;childEnter;');
child.mouseout();
expect(log).toEqual('parentEnter;childEnter;childLeave;');
parent.mouseout();
expect(log).toEqual('parentEnter;childEnter;childLeave;parentLeave;');
});
});
}); });