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.
// These functions chain results into a single
@ -477,53 +521,41 @@ forEach({
dealoc: JQLiteDealoc,
bind: function(element, type, fn){
bind: function bindFn(element, type, fn){
var bind = JQLiteData(element, 'bind');
if (!bind) JQLiteData(element, 'bind', bind = {});
forEach(type.split(' '), function(type){
var eventHandler = bind[type];
if (!eventHandler) {
bind[type] = 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 (type == 'mouseenter' || type == 'mouseleave') {
var mouseenter = bind.mouseenter = createEventHandler(element);
var mouseleave = bind.mouseleave = createEventHandler(element);
var counter = 0;
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);
bindFn(element, 'mouseover', function(event) {
counter++;
if (counter == 1) {
event.type = 'mouseenter';
mouseenter(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 = [];
addEventListenerFn(element, type, eventHandler);
bindFn(element, 'mouseout', function(event) {
counter --;
if (counter == 0) {
event.type = 'mouseleave';
mouseleave(event);
}
});
eventHandler = bind[type];
} else {
eventHandler = bind[type] = createEventHandler(element);
addEventListenerFn(element, type, eventHandler);
}
}
eventHandler.fns.push(fn);
});

View file

@ -603,6 +603,50 @@ describe('jqLite', function() {
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;');
});
});
});