This document discusses how DOM events work under the hood in browsers. It explains that events follow a capture and bubble phase model, where event handlers on parent elements are executed first during capture, then event handlers on the target element, then handlers on parent elements again during bubbling. It provides code for a custom event system that simulates this behavior, handling setting up the event, calculating the parent path, and executing handlers for each phase and the default action. It also covers topics like event data, stopping propagation, and gotchas with cross-browser event handling.
11. // Specify an event handler for a given event and phase
HTMLNode.prototype.addEventListener =
function(eventName, handler, phase){
// Make a __handlers object on
// this element if there is none already
if(!this.__handlers){
this.__handlers = {};
}
// If there are no event handler lists for
// this event, add them
if(!this.__handlers[eventName]){
this.__handlers[eventName] =
{capture : [], bubble: []};
}
// Add the new handler function
// to the specified phase list
this.__handlers[eventName]
[phase ? 'capture' : 'bubble'].push(handler);
}
13. 1. EVENT SETUP
var elements = [],
target = ev.target,
isPropagationStopped = false,
isDefaultPrevented = false;
// We are on the Capture Phase to start with
ev.eventPhase = 1;
ev.stopPropagation = function(){
isPropagationStopped = true;
}
ev.preventDefault = function(){
isDefaultPrevented = true;
}
14. 2. CALCULATE PARENT NODE PATH
// Loop up through the DOM and collect all of the
// parent nodes into the 'elements' array
do{
elements.push(target);
}while((target = target.parentNode));
// Reverse the list so it's a bit easier to read
// in the following for-loop
elements.reverse();
15. 3. EXECUTE CAPTURE PHASE HANDLERS
// For all of the elements in the list...
for(var i = 0 ; i < elements.length; i++){
// If stopPropagation() was called, end the loop -
// we're done.
if(isPropagationStopped){
break;
}
var currentElement = elements[i],
// If there are any event handlers set for
// this element, event type and phase,
// set that array to 'handlers'. Otherwise,
// set 'handlers' to an empty array.
handlers = currentElement.__handlers
&& currentElement.__handlers[ev.type]
&& currentElement.__handlers[ev.type].capture
|| [];
ev.currentTarget = currentElement;
// Loop through the handlers we've collected and
// execute them in the context of the current element
for(var h = 0; i < handlers.length; h++){
handlers[h].call(currentElement, ev);
}
}
16. 4. EXECUTE DOM LEVEL 1 EVENT HANDLER
// If propagation was not stopped, execute
// the DOM level 1 event handler
if(!isPropagationStopped){
ev.target["on" + ev.type].call(ev.target, ev);
}
elements.reverse();
ev.eventPhase = 3;
17. 5. EXECUTE BUBBLE PHASE HANDLERS
// Basically, do the same thing as before,
// but with the 'elements' list reversed...
for(var i = 0 ; i < elements.length; i++){
if(isPropagationStopped){
break;
}
// ... Also, we are working with the 'bubble' phase
// this time, not 'capture'
var currentElement = elements[i],
handlers = currentElement.__handlers
&& currentElement.__handlers[ev.type]
&& currentElement.__handlers[ev.type].bubble
|| [];
ev.currentTarget = currentElement;
for(var h = 0 ; i < handlers.length; h++){
handlers[h].call(currentElement,ev);
}
}
18. 6. EXECUTE DEFAULT BROWSER BEHAVIOR
// Default behaviors for HTML elements
if(!isDefaultPrevented){
// anchor tag (a link)
if(ev.type == "click"
&& ev.target.nodeName.toLowerCase() == "a"){
window.location = ev.target.href;
}
// default cases for the rest of the elements go here...
}
19. DEFAULT ACTIONS
EVENT DEFAULT ACTION
SUBMIT SENDS A POST REQUEST WITH FORM DATA
CLICK ON AN ANCHOR, SETS WINDOW.LOCATION TO THE ELS href
MOUSEDOWN SELECTS TEXT
CONTEXTMENU OPENS BROWSER CONTEXT MENU
KEYDOWN/KEYPRESS TEXT IS WRITTEN INTO INPUT OR TAB TO NEXT TABINDEX
20. EVENT DELEGATION
$('#todo').on('click', '.delete', function(){
// delete the todo
});
22. EVENT DATA
bubbles
Can event bubble?
cancelable
Can default action be prevented?
currentTarget
Current element during bubble or capture
eventPhase
Bubble or capture
target
The original event target
timestamp
Time event created
Type
Name of event
23. EVENT DATA
MOUSE EVENTS
altKey/ctrlKey/metaKey/shiftKey
True if key was pressed
button
Which button was pressed?
clientX/clientY
x/y relative to browser window
relatedTarget
Secondary target (for mouseover its the element entered)
screenX/screenY
x/y relative to computer screen
24. EVENT DATA
KEY EVENTS
charCode
Character representation of key pressed
keyCode
Numeric key code
26. GOTCHAS
PROBLEM $ SOLUTION
IE (8 and below) is almost but not quite exactly unlike
$.fn.on
everything shown
Order of dispatching events per element isnt the same
cross browser jQuery normalizes events
To remove an event, need reference to the original function
$.fn.off
Mouseover an internal element causes a mouseout on the
containing element.
mouseenter/mouseleave