Commit 92aac3ab authored by Brandon Aaron's avatar Brandon Aaron

Fixes memory leaks relating to events in IE with page unload and with jQuery...

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.
parent bb998f25
...@@ -1251,17 +1251,20 @@ jQuery.each({ ...@@ -1251,17 +1251,20 @@ jQuery.each({
remove: function( selector ) { remove: function( selector ) {
if ( !selector || jQuery.filter( selector, [ this ] ).r.length ) { 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 ); this.parentNode.removeChild( this );
} }
}, },
empty: function() { empty: function() {
// Clean up the cache // Remove element nodes and prevent memory leaks
jQuery( "*", this ).each(function(){ jQuery( ">*", this ).remove();
jQuery.removeData(this);
}); // Remove any remaining nodes
while ( this.firstChild ) while ( this.firstChild )
this.removeChild( this.firstChild ); this.removeChild( this.firstChild );
} }
......
...@@ -43,7 +43,7 @@ jQuery.event = { ...@@ -43,7 +43,7 @@ jQuery.event = {
// Init the element's event structure // Init the element's event structure
var events = jQuery.data(element, "events") || jQuery.data(element, "events", {}); 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 // returned undefined or false
var val; var val;
...@@ -149,10 +149,10 @@ jQuery.event = { ...@@ -149,10 +149,10 @@ jQuery.event = {
} else { } else {
var val, ret, fn = jQuery.isFunction( element[ type ] || null ), var val, ret, fn = jQuery.isFunction( element[ type ] || null ),
// Check to see if we need to provide a fake event, or not // 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 // Pass along a fake event
if ( evt ) if ( event )
data.unshift( this.fix({ type: type, target: element }) ); data.unshift( this.fix({ type: type, target: element }) );
// Enforce the right trigger type // Enforce the right trigger type
...@@ -167,7 +167,7 @@ jQuery.event = { ...@@ -167,7 +167,7 @@ jQuery.event = {
val = false; val = false;
// Extra functions don't get the custom event object // Extra functions don't get the custom event object
if ( evt ) if ( event )
data.shift(); data.shift();
// Handle triggering of extra function // Handle triggering of extra function
...@@ -197,23 +197,24 @@ jQuery.event = { ...@@ -197,23 +197,24 @@ jQuery.event = {
var parts = event.type.split("."); var parts = event.type.split(".");
event.type = parts[0]; 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 ); 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 // Pass in a reference to the handler function itself
// So that we can later remove it // So that we can later remove it
args[0].handler = c[j]; args[0].handler = handler;
args[0].data = c[j].data; args[0].data = handler.data;
// Filter the functions by class // Filter the functions by class
if ( !parts[1] || c[j].type == parts[1] ) { if ( !parts[1] || handler.type == parts[1] ) {
var tmp = c[j].apply( this, args ); var ret = handler.apply( this, args );
if ( val !== false ) if ( val !== false )
val = tmp; val = ret;
if ( tmp === false ) { if ( ret === false ) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} }
...@@ -265,9 +266,9 @@ jQuery.event = { ...@@ -265,9 +266,9 @@ jQuery.event = {
// Calculate pageX/Y if missing and clientX/Y available // Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && event.clientX != null ) { if ( event.pageX == null && event.clientX != null ) {
var e = document.documentElement, b = document.body; var doc = document.documentElement, body = document.body;
event.pageX = event.clientX + (e && e.scrollLeft || b && b.scrollLeft || 0); event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0);
event.pageY = event.clientY + (e && e.scrollTop || b && b.scrollTop || 0); event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0);
} }
// Add which for key events // Add which for key events
...@@ -322,54 +323,54 @@ jQuery.fn.extend({ ...@@ -322,54 +323,54 @@ jQuery.fn.extend({
toggle: function() { toggle: function() {
// Save reference to arguments for access in closure // 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 // Figure out which function to execute
this.lastToggle = 0 == this.lastToggle ? 1 : 0; this.lastToggle = 0 == this.lastToggle ? 1 : 0;
// Make sure that clicks stop // Make sure that clicks stop
e.preventDefault(); event.preventDefault();
// and execute the function // 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' // 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 // Check if mouse(over|out) are still within the same parent element
var p = e.relatedTarget; var parent = event.relatedTarget;
// Traverse up the tree // 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 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 // 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 // Bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover); return this.mouseover(handleHover).mouseout(handleHover);
}, },
ready: function(f) { ready: function(fn) {
// Attach the listeners // Attach the listeners
bindReady(); bindReady();
// If the DOM is already ready // If the DOM is already ready
if ( jQuery.isReady ) if ( jQuery.isReady )
// Execute the function immediately // Execute the function immediately
f.apply( document, [jQuery] ); fn.apply( document, [jQuery] );
// Otherwise, remember the function for later // Otherwise, remember the function for later
else else
// Add the function to the wait list // 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; return this;
} }
...@@ -409,11 +410,11 @@ jQuery.extend({ ...@@ -409,11 +410,11 @@ jQuery.extend({
jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," + jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + "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 // Handle event binding
jQuery.fn[o] = function(f){ jQuery.fn[name] = function(fn){
return f ? this.bind(o, f) : this.trigger(o); return fn ? this.bind(name, fn) : this.trigger(name);
}; };
}); });
...@@ -451,3 +452,9 @@ function bindReady(){ ...@@ -451,3 +452,9 @@ function bindReady(){
// A fallback to window.onload, that will always work // A fallback to window.onload, that will always work
jQuery.event.add( window, "load", jQuery.ready ); 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();
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment