JavaScript Async - Events
Written by Ian Elliot   
Monday, 21 August 2017
Article Index
JavaScript Async - Events
Bubbling & Controlling Events
True Asynchronous Events

Bubbling And Capture

We also have to consider the way events work in the DOM. In particular the way events "bubble".

In an HTML page different UI elements are nested inside one another. For example

<div id="div1">
   Some Text
  
<button id="button1">Click me</button>
</div>

defines a button contained inside a div. If we now define an event handler for each:

div1.addEventListener("click",myEventHandler1); button1.addEventListener("click",myEventHandler2);
function myEventHandler1(e){ alert("div"); }
function myEventHandler2(e){ alert("button"); }

you can discover what happens when an event occurs on the inner UI element i.e. the button. 

With event bubbling the inner most item handles the event first and then passes it up in turn to the outer items. So if you click on the button you will see the button alert and then the div alert. 

Why do we want events to bubble?

The reason is that there might be an action which has to be performed by the containing UI element when a child element is used. For example the div event handler might reset all of the child buttons it contains. It might also be the case that you don't want to implement event handlers for all of the inner elements. In this case you could allow the event to bubble up to the outer most UI element and handle it there - notice that the event object passed to the handler can be used to find out what UI element the event actually occurred on. 

For example if  you define three buttons within a div you can handle a click on any button with an event handler registered to the div simply because the button click events bubble up:

<div id="div1">
<button id="button1">Click me</button>
<button id="button2">Click me</button>
<button id="button3">Click me</button>
</div>
<script>
  div1.addEventListener("click",myEventHandler1);
  function myEventHandler1(e){
   alert(e.target.id);
  }
</script>

When you click on any button the alert box correct shows the id of the button. 

By default all events bubble up the UI hierarchy. 

The other way to allow events to propagate is called capture and this can be thought of as bubbling down - sometimes called trickling down. In this case when an event occurs on an inner UI element it is the top most enclosing UI element that gets to handle the event first. 

To select capture you have to specify a third parameter - useCapture - when registering the event handler. So if we change the previous example of a button inside a div to use capture:

<div id="div1">
   Some Text
  
<button id="button1">Click me</button></div>
<script>
 
div1.addEventListener("click",myEventHandler1,true);
 button1.addEventListener("click",myEventHandler2);

 function myEventHandler1(e){ alert("div"); }
  function myEventHandler2(e){ alert("button");
}
</script>

Notice that the div event handler is now registered with useCapture set to true. This means that all click events that occur on UI elements that are contained by it are handled by it and then passed down though child element back to the element that the event occurred on. Now if you click on the button you will see the div message first and the button message second. 

In most cased it is much simpler to use bubbling because older browsers don't support capture. 

Controlling Events

As well as providing information about the event the Event object also has some methods that let you control and enquire about the state of the event.

Many events have a default action. For example clicking on a link loads that page into the browser. You can stop default actions using:

event.preventDefault();

You can discover if some other event handler had prevented the default action using:

event.isDefaultPrevented();

You can also stop bubbling and/or capture using either:

event.stopPropagation()

or:

event.stopImmediatePropagation();

which stops any other event handler being called even if it is attached to the same object.

You can test to see if bubbling has been stopped using:

event.isPropagationStopped();

Compatibility

Event handling in older browsers is a mess.

You can spend a lot of time trying to write event handling code that worked as widely as possible but it is better to simply give in and use jQuery which smooths out the differences.For example in Internet Explorer 8 and earlier you have to use attachEvent in place of add EventListener. There are also big variations in what information is pasesd to the event handler via the event object.

Even in modern browsers there is a great deal of variation in the implementation of the options that are allowed. For example at the time of writing Edge doesn't fully support the options parameter.

There is also the problem of what events are available. The most common events click, focus and so on are supported in a uniform fashion but there are many events relating to specific hardware, mobile and touch hardware in particular that are not implemented uniformly across all browsers. For example only the very latest browsers support WheelEvent - which replaces the nonstandard MouseWheelEvent. Even worse is the TouchEvent which isn't supported on Opera, IE or Safari. Most touch based events are not supported by IE, Opera or Safari.

If you are going to use any touch or other hardware based events other then the very common mouse related events then you need to check which browsers support them.

Custom Events

Normally events are fired by the browser or the more general environment that JavaScript is running in. Sometimes it is useful to be able to originate an event, either an existing event type or a completely new event type under program control.

The simplest way to fire an event is to use the HTMLElement event methods click, focus, blur, submit and reset. All HTMLElements support click but the rest vary in what they support - obvously only a form element supports submit or reset for example.

Firing other events is more problematic as browsers vary in what they support.

The modern way to fire an event is to construct an event object and then pass it as a parameter in dispatchEvent:

var event = new MouseEvent('click', {
                                 'bubbles': true,
                                 'cancelable': true
                                 });  button1.dispatchEvent(event);

If there isn't a constructor for the particular event you want to use simply use the general Event constructor.

var event = new Event('click');
button1.dispatchEvent(event);

The difference is that the most specific constructor has additional methods and properties that make setting up that particular type of event easier.

The older createEvent method is deprecated but still widely supported.

As well as firing predefined events you can also create and fire custom events. All you have to do is use an event name that is suitable and consistent. For example:

var event = new Event('myEvent', {
                  'bubbles': true,
                  'cancelable': true
                 });

button1.addEventListener("myEvent", function (e) {
                        console.log("MyEvent"); });
button1.dispatchEvent(event);

Notice that myEvent isn't a system event but one we just made up.

If you want to include some custom data then you need to use the CustomEvent constructor and pass the data in the detail property:

var event = new CustomEvent('myEvent', {
                               detail: "myData"
                              });

However you don't have to use a CustomEvent object to implement a custom event - only if you want to pass custom data to a custom event.

Asynchronous Custom Events

There is a small problem with custom events - they aren't really events.

Consider the following code:

var event = new Event('myEvent', {
                            'bubbles': true,
                            'cancelable': true
                           });
button1.addEventListener("myEvent", function (e) {
                          console.log("MyEvent");
                          });

console.log("Before Event"); button1.dispatchEvent(event);
console.log("After Event")

what would you expect to see displayed in the console?

What you actually see is:

Before Event
MyEvent
After Event

You might at first glance think that this is what you would expect but wait, the MyEvent message is displayed as the result of an event and the Before and After messages are displayed within the same code which isn't part of the event handler.

What should happen when the event is dispatched is that the event should be added to the dispatch queue and the current code should run to completion. Only then is the UI thread handed back to the dispatcher which then runs what ever event is top of the dispatch queue. In other word what you should see is:
Before Event
After Event
MyEvent

and perhaps some other events might be processed before MyEvnt gets its turn.

The dispatchEvent method is more like a function call than an event invocation. The point is that function calls are synchronous in the sense that the calling function waits for the called function to complete. An even however is supposed to be asynchronous and the function that triggers the event should run to completion before the event is handled. 

coverasync

 <ASIN:1871962528>

<ASIN:1871962501>



Last Updated ( Monday, 21 August 2017 )