Fixes memory leaks relating to events in IE with page unload and with jQuery methods remove, html and empty (#1610, #1618, #1697 and #1731). Also re-worked variable names to be more consistent with the core.
This commit is contained in:
parent
bb998f2518
commit
92aac3abd6
15
src/core.js
15
src/core.js
|
@ -1251,17 +1251,20 @@ jQuery.each({
|
|||
|
||||
remove: function( selector ) {
|
||||
if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) {
|
||||
jQuery.removeData( this );
|
||||
// Prevent memory leaks
|
||||
jQuery( "*", this ).add(this).each(function(){
|
||||
jQuery.event.remove(this);
|
||||
jQuery.removeData(this);
|
||||
});
|
||||
this.parentNode.removeChild( this );
|
||||
}
|
||||
},
|
||||
|
||||
empty: function() {
|
||||
// Clean up the cache
|
||||
jQuery( "*", this ).each(function(){
|
||||
jQuery.removeData(this);
|
||||
});
|
||||
|
||||
// Remove element nodes and prevent memory leaks
|
||||
jQuery( ">*", this ).remove();
|
||||
|
||||
// Remove any remaining nodes
|
||||
while ( this.firstChild )
|
||||
this.removeChild( this.firstChild );
|
||||
}
|
||||
|
|
69
src/event.js
69
src/event.js
|
@ -43,7 +43,7 @@ jQuery.event = {
|
|||
// Init the element's event structure
|
||||
var events = jQuery.data(element, "events") || jQuery.data(element, "events", {});
|
||||
|
||||
var handle = jQuery.data(element, "handle", function(){
|
||||
var handle = jQuery.data(element, "handle") || jQuery.data(element, "handle", function(){
|
||||
// returned undefined or false
|
||||
var val;
|
||||
|
||||
|
@ -149,10 +149,10 @@ jQuery.event = {
|
|||
} else {
|
||||
var val, ret, fn = jQuery.isFunction( element[ type ] || null ),
|
||||
// Check to see if we need to provide a fake event, or not
|
||||
evt = !data[0] || !data[0].preventDefault;
|
||||
event = !data[0] || !data[0].preventDefault;
|
||||
|
||||
// Pass along a fake event
|
||||
if ( evt )
|
||||
if ( event )
|
||||
data.unshift( this.fix({ type: type, target: element }) );
|
||||
|
||||
// Enforce the right trigger type
|
||||
|
@ -167,7 +167,7 @@ jQuery.event = {
|
|||
val = false;
|
||||
|
||||
// Extra functions don't get the custom event object
|
||||
if ( evt )
|
||||
if ( event )
|
||||
data.shift();
|
||||
|
||||
// Handle triggering of extra function
|
||||
|
@ -197,23 +197,24 @@ jQuery.event = {
|
|||
var parts = event.type.split(".");
|
||||
event.type = parts[0];
|
||||
|
||||
var c = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
|
||||
var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
|
||||
args.unshift( event );
|
||||
|
||||
for ( var j in c ) {
|
||||
for ( var j in handlers ) {
|
||||
var handler = handlers[j];
|
||||
// Pass in a reference to the handler function itself
|
||||
// So that we can later remove it
|
||||
args[0].handler = c[j];
|
||||
args[0].data = c[j].data;
|
||||
args[0].handler = handler;
|
||||
args[0].data = handler.data;
|
||||
|
||||
// Filter the functions by class
|
||||
if ( !parts[1] || c[j].type == parts[1] ) {
|
||||
var tmp = c[j].apply( this, args );
|
||||
if ( !parts[1] || handler.type == parts[1] ) {
|
||||
var ret = handler.apply( this, args );
|
||||
|
||||
if ( val !== false )
|
||||
val = tmp;
|
||||
val = ret;
|
||||
|
||||
if ( tmp === false ) {
|
||||
if ( ret === false ) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
@ -265,9 +266,9 @@ jQuery.event = {
|
|||
|
||||
// Calculate pageX/Y if missing and clientX/Y available
|
||||
if ( event.pageX == null && event.clientX != null ) {
|
||||
var e = document.documentElement, b = document.body;
|
||||
event.pageX = event.clientX + (e && e.scrollLeft || b && b.scrollLeft || 0);
|
||||
event.pageY = event.clientY + (e && e.scrollTop || b && b.scrollTop || 0);
|
||||
var doc = document.documentElement, body = document.body;
|
||||
event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0);
|
||||
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0);
|
||||
}
|
||||
|
||||
// Add which for key events
|
||||
|
@ -322,54 +323,54 @@ jQuery.fn.extend({
|
|||
|
||||
toggle: function() {
|
||||
// Save reference to arguments for access in closure
|
||||
var a = arguments;
|
||||
var args = arguments;
|
||||
|
||||
return this.click(function(e) {
|
||||
return this.click(function(event) {
|
||||
// Figure out which function to execute
|
||||
this.lastToggle = 0 == this.lastToggle ? 1 : 0;
|
||||
|
||||
// Make sure that clicks stop
|
||||
e.preventDefault();
|
||||
event.preventDefault();
|
||||
|
||||
// and execute the function
|
||||
return a[this.lastToggle].apply( this, [e] ) || false;
|
||||
return args[this.lastToggle].apply( this, [event] ) || false;
|
||||
});
|
||||
},
|
||||
|
||||
hover: function(f,g) {
|
||||
hover: function(fnOver, fnOut) {
|
||||
|
||||
// A private function for handling mouse 'hovering'
|
||||
function handleHover(e) {
|
||||
function handleHover(event) {
|
||||
// Check if mouse(over|out) are still within the same parent element
|
||||
var p = e.relatedTarget;
|
||||
var parent = event.relatedTarget;
|
||||
|
||||
// Traverse up the tree
|
||||
while ( p && p != this ) try { p = p.parentNode; } catch(e) { p = this; };
|
||||
while ( parent && parent != this ) try { parent = parent.parentNode; } catch(error) { parent = this; };
|
||||
|
||||
// If we actually just moused on to a sub-element, ignore it
|
||||
if ( p == this ) return false;
|
||||
if ( parent == this ) return false;
|
||||
|
||||
// Execute the right function
|
||||
return (e.type == "mouseover" ? f : g).apply(this, [e]);
|
||||
return (event.type == "mouseover" ? fnOver : fnOut).apply(this, [event]);
|
||||
}
|
||||
|
||||
// Bind the function to the two event listeners
|
||||
return this.mouseover(handleHover).mouseout(handleHover);
|
||||
},
|
||||
|
||||
ready: function(f) {
|
||||
ready: function(fn) {
|
||||
// Attach the listeners
|
||||
bindReady();
|
||||
|
||||
// If the DOM is already ready
|
||||
if ( jQuery.isReady )
|
||||
// Execute the function immediately
|
||||
f.apply( document, [jQuery] );
|
||||
fn.apply( document, [jQuery] );
|
||||
|
||||
// Otherwise, remember the function for later
|
||||
else
|
||||
// Add the function to the wait list
|
||||
jQuery.readyList.push( function() { return f.apply(this, [jQuery]); } );
|
||||
jQuery.readyList.push( function() { return fn.apply(this, [jQuery]); } );
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -409,11 +410,11 @@ jQuery.extend({
|
|||
|
||||
jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
|
||||
"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
|
||||
"submit,keydown,keypress,keyup,error").split(","), function(i,o){
|
||||
"submit,keydown,keypress,keyup,error").split(","), function(i, name){
|
||||
|
||||
// Handle event binding
|
||||
jQuery.fn[o] = function(f){
|
||||
return f ? this.bind(o, f) : this.trigger(o);
|
||||
jQuery.fn[name] = function(fn){
|
||||
return fn ? this.bind(name, fn) : this.trigger(name);
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -451,3 +452,9 @@ function bindReady(){
|
|||
// A fallback to window.onload, that will always work
|
||||
jQuery.event.add( window, "load", jQuery.ready );
|
||||
}
|
||||
|
||||
// Prevent memory leaks in IE
|
||||
if ( jQuery.browser.msie )
|
||||
jQuery(window).bind("unload", function() {
|
||||
$("*").add([document, window]).unbind();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue