X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fevent.js;h=f71a1df92e75db30f1584f501aded2560db1b5e7;hb=ffbedf0262b3eea906f39c0115b818d7456a3994;hp=205b510a13936d75c68a8c10a308bcee0d35a7f9;hpb=b4e23b5af0820a66c2e275051f613f3df9a4444d;p=jquery.git diff --git a/src/event.js b/src/event.js index 205b510..f71a1df 100644 --- a/src/event.js +++ b/src/event.js @@ -8,6 +8,9 @@ jQuery.event = { // Bind an event to an element // Original by Dean Edwards add: function(element, type, handler, data) { + if ( element.nodeType == 3 || element.nodeType == 8 ) + return; + // For whatever reason, IE has trouble passing the window object // around, causing it to be cloned in the process if ( jQuery.browser.msie && element.setInterval != undefined ) @@ -19,7 +22,7 @@ jQuery.event = { // if data is passed, bind to handler if( data != undefined ) { - // Create temporary function pointer to original handler + // Create temporary function pointer to original handler var fn = handler; // Create unique handler function, wrapped around original handler @@ -41,36 +44,34 @@ jQuery.event = { handler.type = parts[1]; // Init the element's event structure - if (!element.$events) - element.$events = {}; + var events = jQuery.data(element, "events") || jQuery.data(element, "events", {}); - if (!element.$handle) - element.$handle = function() { - // returned undefined or false - var val; - - // Handle the second event of a trigger and when - // an event is called after a page has unloaded - if ( typeof jQuery == "undefined" || jQuery.event.triggered ) - return val; - - val = jQuery.event.handle.apply(element, arguments); - + var handle = jQuery.data(element, "handle") || jQuery.data(element, "handle", function(){ + // returned undefined or false + var val; + + // Handle the second event of a trigger and when + // an event is called after a page has unloaded + if ( typeof jQuery == "undefined" || jQuery.event.triggered ) return val; - }; + + val = jQuery.event.handle.apply(element, arguments); + + return val; + }); // Get the current list of functions bound to this event - var handlers = element.$events[type]; + var handlers = events[type]; // Init the event handler queue if (!handlers) { - handlers = element.$events[type] = {}; + handlers = events[type] = {}; // And bind the global event handler to the element if (element.addEventListener) - element.addEventListener(type, element.$handle, false); + element.addEventListener(type, handle, false); else - element.attachEvent("on" + type, element.$handle); + element.attachEvent("on" + type, handle); } // Add the function to the element's handler list @@ -85,7 +86,11 @@ jQuery.event = { // Detach an event or set of events from an element remove: function(element, type, handler) { - var events = element.$events, ret, index; + // don't do events on text and comment nodes + if ( element.nodeType == 3 || element.nodeType == 8 ) + return; + + var events = jQuery.data(element, "events"), ret, index; // Namespaced event handlers if ( typeof type == "string" ) { @@ -111,7 +116,7 @@ jQuery.event = { // remove all handlers for the given type else - for ( handler in element.$events[type] ) + for ( handler in events[type] ) // Handle the removal of namespaced events if ( !parts[1] || events[type][handler].type == parts[1] ) delete events[type][handler]; @@ -120,9 +125,9 @@ jQuery.event = { for ( ret in events[type] ) break; if ( !ret ) { if (element.removeEventListener) - element.removeEventListener(type, element.$handle, false); + element.removeEventListener(type, jQuery.data(element, "handle"), false); else - element.detachEvent("on" + type, element.$handle); + element.detachEvent("on" + type, jQuery.data(element, "handle")); ret = null; delete events[type]; } @@ -130,8 +135,10 @@ jQuery.event = { // Remove the expando if it's no longer used for ( ret in events ) break; - if ( !ret ) - element.$handle = element.$events = null; + if ( !ret ) { + jQuery.removeData( element, "events" ); + jQuery.removeData( element, "handle" ); + } } }, @@ -147,29 +154,41 @@ jQuery.event = { // Handle triggering a single element } else { + // don't do events on text and comment nodes + if ( element.nodeType == 3 || element.nodeType == 8 ) + return; + 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 + data[0].type = type; + // Trigger the event - if ( jQuery.isFunction( element.$handle ) ) - val = element.$handle.apply( element, data ); + if ( jQuery.isFunction( jQuery.data(element, "handle") ) ) + val = jQuery.data(element, "handle").apply( element, data ); // Handle triggering native .onfoo handlers if ( !fn && element["on"+type] && element["on"+type].apply( element, data ) === false ) val = false; // Extra functions don't get the custom event object - if ( evt ) + if ( event ) data.shift(); // Handle triggering of extra function - if ( extra && extra.apply( element, data ) === false ) - val = false; + if ( extra ) { + // call the extra function and tack the current return value on the end for possible inspection + var ret = extra.apply( element, data.concat( val ) ); + // if anything is returned, give it precedence and have it overwrite the previous value + if (ret !== undefined) + val = ret; + } // Trigger the native events (except for clicks on links) if ( fn && donative !== false && val !== false && !(jQuery.nodeName(element, 'a') && type == "click") ) { @@ -194,23 +213,24 @@ jQuery.event = { var parts = event.type.split("."); event.type = parts[0]; - var c = this.$events && 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(); } @@ -249,11 +269,11 @@ jQuery.event = { }; // Fix target property, if necessary - if ( !event.target && event.srcElement ) - event.target = event.srcElement; + if ( !event.target ) + event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either // check if target is a textnode (safari) - if (jQuery.browser.safari && event.target.nodeType == 3) + if ( event.target.nodeType == 3 ) event.target = originalEvent.target.parentNode; // Add relatedTarget, if necessary @@ -262,9 +282,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.scrollLeft || 0); - event.pageY = event.clientY + (e && e.scrollTop || b.scrollTop || 0); + var doc = document.documentElement, body = document.body; + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientLeft || 0); } // Add which for key events @@ -319,54 +339,55 @@ 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, arguments ) || 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 true; // 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; } @@ -397,12 +418,8 @@ jQuery.extend({ jQuery.readyList = null; } // Remove event listener to avoid memory leak - if ( jQuery.browser.mozilla || jQuery.browser.opera ) + if ( document.removeEventListener ) document.removeEventListener( "DOMContentLoaded", jQuery.ready, false ); - - // Remove script element used by IE hack - if( !window.frames.length ) // don't remove if frames are present (#1187) - jQuery(window).load(function(){ jQuery("#__ie_init").remove(); }); } } }); @@ -410,11 +427,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); }; }); @@ -424,49 +441,34 @@ function bindReady(){ if ( readyBound ) return; readyBound = true; - // If Mozilla is used - if ( jQuery.browser.mozilla || jQuery.browser.opera ) + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) // Use the handy event callback document.addEventListener( "DOMContentLoaded", jQuery.ready, false ); - // If IE is used, use the excellent hack by Matthias Miller - // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited - else if ( jQuery.browser.msie ) { - - // Only works if you document.write() it - document.write("<\/script>"); - - // Use the defer script hack - var script = document.getElementById("__ie_init"); - - // script does not exist if jQuery is loaded dynamically - if ( script ) - script.onreadystatechange = function() { - if ( this.readyState != "complete" ) return; - jQuery.ready(); - }; - - // Clear from memory - script = null; - - // If Safari is used - } else if ( jQuery.browser.safari ) - // Continually check to see if the document.readyState is valid - jQuery.safariTimer = setInterval(function(){ - // loaded and complete are both valid states - if ( document.readyState == "loaded" || - document.readyState == "complete" ) { - - // If either one are found, remove the timer - clearInterval( jQuery.safariTimer ); - jQuery.safariTimer = null; - - // and execute any waiting functions - jQuery.ready(); - } - }, 10); + // If Safari or IE is used + // Continually check to see if the document is ready + if (jQuery.browser.msie || jQuery.browser.safari ) (function(){ + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + if ( jQuery.browser.msie || document.readyState != "loaded" && document.readyState != "complete" ) + document.documentElement.doScroll("left"); + } catch( error ) { + return setTimeout( arguments.callee, 0 ); + } + + // and execute any waiting functions + jQuery.ready(); + })(); // A fallback to window.onload, that will always work jQuery.event.add( window, "load", jQuery.ready ); } + +// Prevent memory leaks in IE +// And prevent errors on refresh with events like mouseover +// Window isn't included so as not to unbind existing unload events +jQuery(window).bind("unload", function() { + jQuery("*").add(document).unbind(); +});