Event Listener behavior is a nightmare. They vary wildly in implementation, especially regarding the effect of the stopPropagation() method. They don't seem reliable AT ALL.

From pg 125 the book states that stopPropagation() "prevents those clicks from bubbling up to the document element...". This leads one to believe that stopPropagation() effects only Bubble Phase listeners. However, behavior would lead one to believe that it also stops Capture Phase listeners of the *next* (not current) page element as well (with the exception of some bugs). This should be clarified.

I composed a web page to attempt to expose how events occur. I did not expect to see the unexpected behavior I discovered. I've spent a number of days pulling my hair out trying to make rhyme or reason of the behaviors.

There is no indication in this chapter of what a Hornet's nest event listeners are - I strongly suggest there be one. I'm attaching the code I used to expose the events. There are notes in the web page itself to indicate what and where certain behaviors happen.

I'd be very interested in hearing what other people might have to say.

HTH,
Cosmo Lee

MYlinklistener.html

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>The Event Listener Hornet's Nest</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script type="text/javascript" src="core.js"></script>
    <script type="text/javascript" src="MYlinklistener.js"></script>
  </head>
  <body>
	<h1>Event Listeners are a hornet's nest!</h1>  
	<h4>They vary wildly in implementation, especially regarding the effect of the stopPropagation() method.  They don't seem reliable AT ALL.  This page was composed to demonstrate the scope and order of Capture Phase and Bubble Phase event execution.  It requires "core.js" from the "Simply Javascript" book, to prevent execution of javascript code prior to all XHTML loading, and "MYlinklistener.js", which I composed.  These tests are not relevant to Internet Explorer, since it doesn't have a "capture phase" for events.  The described behaviors were tested on Opera 9.22 & Firefox 1.5 & 2.0.0.2</h4>

	<p>From pg 125 the book states that stopPropagation() "prevents those clicks from bubbling up to the document element...".  This leads one to believe that stopPropagation() effects only Bubble Phase listeners.  However, behavior here would lead one to believe that it also stops Capture Phase listeners of the *next* (not current) page element as well (with the exception of some bugs).</p?>
 
	<div  id="divListen">

		<p>The Div starts here, it has an id of #divListen, it has event listeners, but the paragraph itself has none.  In Opera, Firefox: if you click here, the div's Capture Phase and Bubble Phase listeners will be called - Capture Phase listeners executed first, then Bubble Phase listeners - as expected, according to the W3C event model.  Bubble Phase events may be optionally cancelled.  Click in the WHITESPACE below: Opera: inexplicably (we're still in the div), only the div's *Bubble Phase* events are called. Capture Phase events don't execute at all(!).  Firefox: *all* div listeners are called, in javascript source code *event-handler-assignment-order*(!).  ie: if you assign Bubble Phase listeners to an element before Capture Phase listeners, then the Bubble Phase listeners will execute first - and obviously you can't cancel propagation of Bubble Phase events, because they've already occured before Capture Phase events give you the option!  If you assign Capture Phase listeners before Event Phase listeners, they execute in that order, but stopPropagation() *fails* to cancel propagation of Bubble Phase events.
		</p>

	<br /><br />

		<p>This paragraph has no listeners assigned, but is in the Div.  Click here and the listeners will behave as in the previous paragraph.  However, if you click in the whitespace above or below, browsers will behave unexpectedly.</p>

	<br /><br />

	<p id="paraListen">
		This paragraph has and id of #paraListen, it has event listeners.  Click variously on this Paragraph, the Link, and the Div above to see which listeners are involved and their order of execution. Clicking the PARAGRAPH: Opera: The Div listeners (Capture & Bubble) get called, but the #paraListen Capture Phase listeners don't run at all - only the Bubble Phase listener runs(!).  Firefox: if you cancel propagation from the #divListen listener, *both* Capture Phase and Bubble Phase events will be cancelled for event listners of both #paraListen and #linkListen(!).  The remaining Capture phase listener (02) of the Div, does NOT get cancelled, but the Div's Bubble Phase listener does.  However, if you cancel propagation from the #paraListen Capture Phase listener, the #paraListen Bubble Phase listener doesn't get cancelled(!), although the #divListener bubble event does.  The following link has an id of #linkListen, it has event listeners.  Clicking the LINK: Opera & Firefox: cancelling propagation from #divListen will cause *both* Capture Phase and Bubble Phase events to be cancelled for the both #paraListen & #linkListen(!). Cancelling propagation from #paraListen will cause the #paraListen Bubble Phase event to be cancelled (unlike when clicking in the paragraph), and *both* Capture Phase and Bubble Phase events to be cancelled for the #linkListen(!).  Cancelling propagation from #linkListen will not cancel the #linkListen Bubble Phase listener as expected(!), but it does cancel the Bubble Phase events for #paraListen and #divListen.: <a id="linkListen"
          href="http://en.wikipedia.org/wiki/Christopher_Pike">Christopher
      Pike</a>.
	</p>
	<br /><br /><br />
	<p>The Div ends here.</p>
	</div>
	<p>I conclude that if you wish to rely on stopPropagation() to reliably control optional execution logic, you are out of luck.  And in the case of Opera, you can't count on Capture Phase events to function. </p>
	<p>08-19-2007: cosmolee at bigfoot dot com</p>

  </body>
</html>

MYlinklistener.js:

Code:
/*
2007-08-19: Cosmo Lee:
This code demonstrates the scope and order of Capture Phase and Bubble Phase event execution.
This does not apply to IE, since IE doesn't have a Capture Phase, %-{ therefore, I've taken out
any IE detection code that this code is derived from in the "Simply Javascript" book.  
*/

var myObj = {
  init: function()
  {
//Set "#linkListen" to have event listener:
    var link = document.getElementById("linkListen");
      link.addEventListener("click", myObj.clickCapturePhaseListener01, true); //Capture Phase
      link.addEventListener("click", myObj.clickBubblePhaseListener01, false); //Bubble Phase

//Set "#paraListen" to have event listener:
		link = document.getElementById("paraListen");
      link.addEventListener("click", myObj.clickCapturePhaseListener01, true); //Capture Phase
      link.addEventListener("click", myObj.clickBubblePhaseListener01, false); //Bubble Phase
    
//Set "#divListen" to have event listener:
		link = document.getElementById("divListen");
/*
NOTE: 
Firefox: the order of these declaration affects the order of execution: try switching the source code order of clickCapturePhaseListener01 with clickBubblePhaseListener01 to see effects.
*/
      link.addEventListener("click", myObj.clickBubblePhaseListener01, false); //Bubble Phase
      link.addEventListener("click", myObj.clickCapturePhaseListener01, true); //Capture Phase
      link.addEventListener("click", myObj.clickCapturePhaseListener02, true); //Capture Phase
  },

  clickCapturePhaseListener01: function(event)
  {
	var myId = this.id;
		if (!confirm("#" + myId + ": clickCapturePhaseListener01(): Continue to propagate?"))
			{
			alert("clickCapturePhaseListener01(): \nYou chose to CANCEL propagation.\nI will now call stopPropagation(), and stop Bubble propagation.");
			if (typeof event.stopPropagation == "undefined")
				{
				alert("event.stopPropagation is undefined!");
				}
			else
				{
				event.stopPropagation();
				}
			}
  },


  clickCapturePhaseListener02: function(event)
  {
	var myId = this.id;
	alert("#" + myId + ": clickCapturePhaseListener02()");
  },


  clickBubblePhaseListener01: function(event)
  {
	var myId = this.id;
    alert("#" + myId + ": clickBubblePhaseListener01()");
  }
};

Core.start(myObj);