A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4 年之前
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. title: Javascript - Event order
  2. url: http://www.quirksmode.org/js/events_order.html
  3. hash_url: a90e5a0df79fca21cb3a03be83b360fc
  4. <p class="intro">On the <a href="introevents.html">Introduction to events</a> page&#13;I asked a question that at first sight seems incomprehensible:&#13;“If an element and one of its ancestors have an event handler for the same event, which&#13;one should fire first?” Not surprisingly, this depends on the browser.</p>&#13;&#13;<p>The basic problem is very simple. Suppose you have a element inside an element</p>
  5. <pre>
  6. -----------------------------------
  7. | element1 |
  8. | ------------------------- |
  9. | |element2 | |
  10. | ------------------------- |
  11. | |
  12. -----------------------------------
  13. </pre>&#13;&#13;<p>and both have an onClick event handler. If the user clicks on element2 he causes&#13;a click event in both element1 and element2. But which event fires first? Which event handler should&#13;be executed first? What, in other words, is the <em>event order</em>?</p>&#13;&#13;<h3>Two models</h3>&#13;&#13;<p>Not surprisingly, back in the bad old days Netscape and Microsoft came to different conclusions.</p>&#13;&#13;<ul><li>Netscape said that the event on element1 takes place first. This is called&#13;event <em>capturing</em>.</li>&#13;<li>Microsoft maintained that the event on element2 takes precedence. This is called&#13;event <em>bubbling</em>.</li>&#13;</ul>&#13;&#13;<p>The two event orders are radically opposed. Explorer&#13;only supports event bubbling. Mozilla, Opera 7 and Konqueror support both. Older Opera's and&#13;iCab support neither.</p>&#13;&#13;<h4>Event capturing</h4>&#13;&#13;<p>When you use event capturing</p>&#13;&#13;
  14. <pre>
  15. | |
  16. ---------------| |-----------------
  17. | element1 | | |
  18. | -----------| |----------- |
  19. | |element2 \ / | |
  20. | ------------------------- |
  21. | Event CAPTURING |
  22. -----------------------------------
  23. </pre>&#13;&#13;<p>the event handler of element1 fires first, the event handler of element2 fires last.</p>&#13;&#13;&#13;<h4>Event bubbling</h4>&#13;&#13;<p>When you use event bubbling</p>&#13;&#13;
  24. <pre>
  25. / \
  26. ---------------| |-----------------
  27. | element1 | | |
  28. | -----------| |----------- |
  29. | |element2 | | | |
  30. | ------------------------- |
  31. | Event BUBBLING |
  32. -----------------------------------
  33. </pre>&#13;&#13;<p>the event handler of element2 fires first, the event handler of element1 fires last.</p>&#13;&#13;<h3>W3C model</h3>&#13;&#13;<p>W3C has very sensibly decided to take a middle position in this struggle.&#13;Any event taking place in the&#13;<a href="http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/" class="external">W3C event model</a> is first captured until it&#13;reaches the target element and then bubbles up again.</p>&#13;&#13;
  34. <pre>
  35. | | / \
  36. -----------------| |--| |-----------------
  37. | element1 | | | | |
  38. | -------------| |--| |----------- |
  39. | |element2 \ / | | | |
  40. | -------------------------------- |
  41. | W3C event model |
  42. ------------------------------------------
  43. </pre>&#13;&#13;<p>You, the web developer, can choose whether to register an event handler in the capturing or&#13;in the bubbling phase. This is done through the <code>addEventListener()</code>&#13;method explained on the <a href="events_advanced.html">Advanced models</a> page.&#13;If its last argument is <code>true</code> the event handler is set for the capturing phase, if it is&#13;<code>false</code> the event handler is set for the bubbling phase.</p>&#13;&#13;<p>Suppose you do</p>&#13;&#13;<pre>&#13;element1.addEventListener('click',doSomething2,true)&#13;element2.addEventListener('click',doSomething,false)&#13;</pre>&#13;&#13;<p>If the user clicks on element2 the following happens:</p>&#13;&#13;<ol>&#13;<li>The <code>click</code> event starts in the capturing phase. The event looks if any&#13;ancestor element of element2 has a <code>onclick</code> event handler for the capturing&#13;phase.</li>&#13;<li>The event finds one on element1. <code>doSomething2()</code> is&#13;executed.</li>&#13;<li>The event travels down to the target itself, no more event handlers for the capturing&#13;phase are found. The event moves to its bubbling phase and executes <code>doSomething()</code>,&#13;which is registered to element2 for the bubbling phase.</li>&#13;<li>The event travels upwards again and checks if any ancestor element of the&#13;target has an event handler for the bubbling phase. This is not the case, so nothing happens.</li>&#13;</ol>&#13;&#13;&#13;<p>The reverse would be</p>&#13;&#13;<pre>&#13;element1.addEventListener('click',doSomething2,false)&#13;element2.addEventListener('click',doSomething,false)&#13;</pre>&#13;&#13;<p>Now if the user clicks on element2 the following happens:</p>&#13;&#13;<ol>&#13;<li>The <code>click</code> event starts in the capturing phase. The event looks if any&#13;ancestor element of element2 has a <code>onclick</code> event handler for the capturing&#13;phase and doesn’t find any.</li>&#13;<li>The event travels down to the target itself.&#13;The event moves to its bubbling phase and executes <code>doSomething()</code>,&#13;which is registered to element2 for the bubbling phase.</li>&#13;<li>The event travels upwards again and checks if any ancestor element of the&#13;target has an event handler for the bubbling phase.</li>&#13;<li>The event finds one on element1. Now <code>doSomething2()</code> is executed.</li>&#13;</ol>&#13;&#13;<h4>Compatibility with traditional model</h4>&#13;&#13;<p>In the browsers that support the W3C DOM, a traditional event registration</p>&#13;&#13;<pre>&#13;element1.onclick = doSomething2;&#13;</pre>&#13;&#13;<p>is seen as a registration in the <em>bubbling phase</em>.</p>&#13;&#13;&#13;<h3>Use of event bubbling</h3>&#13;&#13;<p>Few web developers consciously use event capturing or bubbling. In Web pages as they are made&#13;today, it is simply not necessary to let a bubbling event be handled by several different event handlers.&#13;Users might get confused by several things happening after one mouse click, and usually you want&#13;to keep your event handling scripts separated. When the user clicks on an element, something happens,&#13;when he clicks on another element, something else happens.</p>&#13;&#13;<p>Of course this might change in the future, and it’s good to have models available that&#13;are forward compatible. But the main practical use of event capturing and bubbling today&#13;is the registration of default functions.</p>&#13;&#13;<h3>It always happens</h3>&#13;&#13;<p>What you first need to understand is that event capturing or bubbling always happens.&#13;If you define a general onclick event handler for your entire document</p>&#13;&#13;<pre>&#13;document.onclick = doSomething;&#13;if (document.captureEvents) document.captureEvents(Event.CLICK);&#13;</pre>&#13;&#13;<p>any <code>click</code> event on any element in the document will eventually bubble up to the document and&#13;thus fire this general event handler.&#13;Only when a previous event handling script explicitly orders the event to stop bubbling, it will not propagate to the&#13;document.</p>&#13;&#13;<h3>Uses</h3>&#13;&#13;<p>Because any event ends up on the document, default event handlers become possible.&#13;Suppose you have this page:</p>&#13;&#13;
  44. <pre>
  45. ------------------------------------
  46. | document |
  47. | --------------- ------------ |
  48. | | element1 | | element2 | |
  49. | --------------- ------------ |
  50. | |
  51. ------------------------------------
  52. element1.onclick = doSomething;
  53. element2.onclick = doSomething;
  54. document.onclick = defaultFunction;
  55. </pre>
  56. &#13;<p>Now if the user clicks on element1 or 2, <code>doSomething()</code> is executed. You can&#13;stop the event propagation here, if you wish. If you don’t the event bubbles up to&#13;<code>defaultFunction()</code>.&#13;If the user clicks anywhere else <code>defaultFunction()</code> is also executed. This might be&#13;useful sometimes.</p>&#13;&#13;&#13;<p>Setting document–wide event handlers is necessary in drag–and–drop&#13;scripts. Typically a <code>mousedown</code> event on a layer selects this layer and makes&#13;it respond to the <code>mousemove</code> event. Though the&#13;<code>mousedown</code> is usually registered on the layer to avoid browser bugs, both&#13;other event handlers must be document–wide.</p>&#13;&#13;<p>Remember the First Law of Browserology: anything can happen, and it usually does when you’re&#13;least prepared for it. So it may happen that the user moves his mouse very wildly and the&#13;script doesn’t keep up so that the mouse is not over the layer any more.</p>&#13;&#13;<ul>&#13;<li>If the <code>onmousemove</code> event handler is registered to the layer,&#13;the layer doesn’t react to the mouse movement any more, causing confusion.</li>&#13;&#13;<li>If the <code>onmouseup</code> event handler is registered on the layer,&#13;this event isn’t caught either so that the layer keeps reacting to the mouse&#13;movements even after the user thinks he dropped the layer. This causes even more confusion.</li>&#13;</ul>&#13;&#13;<p>So in this case event bubbling is very useful because registering&#13;your event handlers on document level makes sure they’re always executed.</p>&#13;&#13;<h3>Turning it off</h3>&#13;&#13;<p>But usually you want to turn all capturing and bubbling off to keep functions from&#13;interfering with each other.&#13;Besides, if your document structure is very complex (lots of nested tables and such) you may save system&#13;resources by turning off bubbling. The browser has to go through every single ancestor element&#13;of the event target to see if it has an event handler. Even if none are found, the search still takes time.</p>&#13;&#13;<p>In the Microsoft model you must set the event’s <code>cancelBubble</code> property to true.</p>&#13;&#13;<pre>&#13;window.event.cancelBubble = true&#13;</pre>&#13;&#13;<p>In the W3C model you must call the event’s <code>stopPropagation()</code> method. </p>&#13;&#13;<pre>&#13;e.stopPropagation()&#13;</pre>&#13;&#13;<p>This stops all propagation of the event in the bubbling phase. For a complete cross-browser experience do</p>&#13;&#13;<pre>&#13;function doSomething(e)&#13;{&#13; if (!e) var e = window.event;&#13; e.cancelBubble = true;&#13; if (e.stopPropagation) e.stopPropagation();&#13;}&#13;</pre>&#13;&#13;<p class="smaller">Setting the <code>cancelBubble</code> property in browsers that don’t support it doesn’t hurt.&#13;The browser shrugs and creates the property. Of course it doesn’t actually&#13;cancel the bubbling, but the assignment itself is safe.</p>&#13;&#13;<h3>currentTarget</h3>&#13;&#13;<p>As we’ve seen earlier, an event has a <code>target</code> or <code>srcElement</code> that&#13;contains a reference to the element the event happened on. In our example this is element2,&#13;since the user clicked on it.</p>&#13;&#13;<p>It is very important to understand that during the capturing and bubbling phases (if any)&#13;this target does not change: it always remains a reference to element2.</p>&#13;&#13;<p>But suppose we register these event handlers:</p>&#13;&#13;<pre>&#13;element1.onclick = doSomething;&#13;element2.onclick = doSomething;&#13;</pre>&#13;&#13;<p>If the user clicks on element2 <code>doSomething()</code> is executed twice. But how&#13;do you know which HTML element is currently handling the event? <code>target/srcElement</code>&#13;don’t give a clue, they always refer to element2 since it is the original source of the&#13;event.</p>&#13;&#13;<p>To solve this problem W3C has added the <code>currentTarget</code> property. It contains a&#13;reference to the HTML element the event is currently being handled by: exactly what we need.&#13;Unfortunately the Microsoft model doesn’t contain a similar property.</p>&#13;&#13;<p>You can also use&#13;<a href="this.html">the <code>this</code> keyword</a>.&#13;In the example above it refers to the HTML element the event is handled on, just like&#13;<code>currentTarget</code>.</p>&#13;&#13;<h3>Problems of the Microsoft model</h3>&#13;&#13;<p>But when you use the Microsoft event registration model&#13;the <code>this</code> keyword doesn’t refer to the HTML element. Combined with the&#13;lack of a <code>currentTarget</code>–like property in the Microsoft model,&#13;this means that if you do</p>&#13;&#13;<pre>&#13;element1.attachEvent('onclick',doSomething)&#13;element2.attachEvent('onclick',doSomething)&#13;</pre>&#13;&#13;<p>you <em>cannot</em> know which HTML element currently handles the event. This is the most&#13;serious problem with the Microsoft event registration model and for me it’s reason enough&#13;never to use it, not even in IE/Win only applications.</p>&#13;&#13;<p>I hope Microsoft will soon add a <code>currentTarget</code>–like property — or maybe&#13;even follow the standard? Web developers need this information.</p>&#13;&#13;<h3>Continue</h3>&#13;&#13;<p>If you wish to go through all event pages in order, you should now continue with the&#13;<a href="events_mouse.html">Mouse events</a> page.</p>