Memory and Performance

Event delegation

The solution to “too many event handlers” is to use event delegates. Event delegation utilizes event bubbling to manage one type of event with just one event handler.

For example, the Click event bubbles to document. This means that you can specify an onclick event handler for the entire page rather than each clickable element individually. For example, you have the following HTML:

<ul id="myLinks"> 
     <li id="goSomewhere">Go somewhere</li> 
     <li id="doSomething">Do something</li> 
     <li id="sayHi">Say hi</li> 
</ul>
Copy the code

The HTML here contains three list items that should do something when clicked. A common practice for this is to specify three event handlers like this:

let item1 = document.getElementById("goSomewhere"); 
let item2 = document.getElementById("doSomething"); 
let item3 = document.getElementById("sayHi"); 
item1.addEventListener("click".(event) = > { 
 location.href = "http:// www.wrox.com"; 
}); 
item2.addEventListener("click".(event) = > { 
 document.title = "I changed the document's title"; 
}); 
item3.addEventListener("click".(event) = > { 
 console.log("hi"); 
});
Copy the code

If you do the same for all the elements on the page that need to use the onclick event handler, you end up with a lot of identical code that only specifies the event handler. Using event delegates, you can solve the problem by simply adding an event handler to the common ancestor node of all elements. Such as:

let list = document.getElementById("myLinks");
list.addEventListener("click".(event) = > {
    let target = event.target;
    switch (target.id) {
        case "doSomething":
            document.title = "I changed the document's title";
            break;
        case "goSomewhere":
            location.href = "http:// www.wrox.com";
            break;
        case "sayHi":
            console.log("hi");
            break; }});Copy the code

Here only to

<div id="myDiv"> 
 	<input type="button" value="Click Me" id="myBtn"> 
</div> 
<script type="text/javascript"> 
     let btn = document.getElementById("myBtn"); 
     btn.onclick = function() { 
    	 // Perform the operation
     	document.getElementById("myDiv").innerHTML = "Processing..."; 
     	/ / not good!
     }; 
</script>
Copy the code

The button here is in

Element. Clicking the button removes and replaces itself with a message to prevent double-clicking. This is a common practice on many websites. The problem is that the button is still associated with an event handler after it is removed. in

Setting innerHTML on the element removes the button completely, but the event handler still hangs on top of the button. Some browsers, especially IE8 and earlier versions, have problems at this point. It is possible that references to elements and references to event handlers will remain in memory. If you know an element will be deleted, it is a good idea to manually remove its event handler before deleting it, such as:

<div id="myDiv">
  <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
  let btn = document.getElementById("myBtn");
  btn.onclick = function () {
    // Perform the operation
    btn.onclick = null; // Delete the event handler
    document.getElementById("myDiv").innerHTML = "Processing...";
  }; 
</script>
Copy the code

In the overwritten example, set

The button’s event handler is removed before the innerHTML attribute of the element. This ensures that memory is reclaimed and the button can be safely removed from the DOM.

Note, however, that the delete button in the event handler prevents the event from bubbling. Events bubble only if the event target still exists in the document.

Note that event delegation can also help solve this problem. If you know in advance that a section of the page will be deleted using innerHTML, don’t simply add event handlers to elements in that section. Adding event handlers to higher-level nodes can also handle events in this region.

Another problem that can cause residual references in memory is page unloading. Again, IE8 and earlier have a lot of problems in this case, but it seems to affect all browsers. If event handlers are not cleaned up after the page is unloaded, they remain in memory. After that, the number of residual objects in memory increases each time the browser loads and unloads the page (such as by moving forward, backward, or refreshing), because the event handler is not recycled.

In general, it is best to remove all event handlers in the OnUnload event handler before the page is unloaded. This is also a good time to use event delegates, because there are few event handlers, so it’s easy to remember which ones to delete. One thing to remember about cleaning up when a page is unloaded is that what is done in the OnLoad event handler is best recovered in the OnUnload event handler.

Simulation of events

DOM event simulation

At any time, you can create an event object using the document.createEvent() method. This method takes a parameter that is a string representing the type of event to be created.

  •  “UIEvents” (“UIEvent” in DOM3) : Generic user interface events (both mouse and keyboard events inherit from this event).
  •  “MouseEvents” (“MouseEvents” in DOM3) : generic MouseEvents.
  •  “HTMLEvents” (not in DOM3) : Generic HTML events (HTML events have been scattered into other event categories).

Triggering an event requires a call: use the dispatchEvent() method, which exists on all DOM nodes that support events. The dispatchEvent() method takes a parameter that represents the event object to fire the event. After calling the dispatchEvent() method, the event is “normalized” and bubbles up to trigger event handler execution.

Simulated Mouse events

Emulating a mouse event requires creating a new mouse Event object and then initializing it with the necessary information. To create a mouse event object, call the createEvent() method and pass in the “MouseEvents” parameter. This returns an event object with an initMouseEvent() method that specifies mouse-specific information for the new object.

The initMouseEvent() method takes 15 parameters corresponding to the properties that mouse events expose. These parameters are listed below.

  •  type (string) : The type of event to fire, such as “click”.
  •  bubbles (Boolean) : indicates whether an event bubbles. To accurately simulate mouse events, this should be set to true.
  •  cancelable (Boolean) : indicates whether an event can be cancelled. To accurately simulate mouse events, this should be set to true.
  •  view (AbstractView) : View associated with an event. It’s basically always document.DefaultView.
  •  detail (integer) : additional information about an event. Used only by event handlers, usually 0.
  •  screenX (integer) : Events relative to the screenxCoordinates.
  •  screenY (integer) : events relative to the screenyCoordinates.
  •  clientX (integer) : The event relative to the viewportxCoordinates.
  •  clientY (integer) : Event relative to viewportyCoordinates.
  •  CTRlKey (Boolean) : Indicates whether Ctrl is pressed. The default is false.
  •  altkey (Boolean) : Indicates whether the Alt key is pressed. The default is false.
  •  Shiftkey (Boolean) : Indicates whether the Shift key is pressed. The default is false.
  •  metakey (Boolean value) : indicates whether the Meta key is pressed. The default is false.
  •  button (integer) : indicates the button that is pressed. The default is 0.
  •  relatedTarget: The object associated with the event. Only used when simulating mouseover and Mouseout.

Obviously, these parameters of the initMouseEvent() method correspond one-to-one to the event object property of the mouse event. The first four parameters are the only ones that matter to properly simulate the event, because they are used by the browser; the other parameters are used by the event handler. The target property of the Event object is automatically set to the node passed in when the dispatchEvent() method is called. Here’s an example of emulating a click event with default values:

let btn = document.getElementById("myBtn"); 
// Create an event object
let event = document.createEvent("MouseEvents"); 
// Initialize the event object
event.initMouseEvent("click".true.true.document.defaultView, 
 0.0.0.0.0.false.false.false.false.0.null); 
// Trigger the event
btn.dispatchEvent(event);
Copy the code

All mouse events, including DBLClick, can be modeled like this in a DOM-compliant browser.

Simulated keyboard events

Keyboard events are created in DOM3 by passing the parameter “KeyboardEvent” to the createEvent() method. This returns an event object with an initKeyboardEvent() method. This method takes the following parameters.

  •  type (string) : indicates the event type to be triggered, for example, keyDown.
  •  bubbles (Boolean) : indicates whether an event bubbles. To accurately simulate keyboard events, this should be set to true.
  •  cancelable (Boolean) : indicates whether an event can be cancelled. To accurately simulate keyboard events, this should be set to true.
  •  view (AbstractView) : View associated with an event. It’s basically always document.DefaultView.
  •  key (string) : string code for pressing a key.
  •  location (integer) : the location where the button is pressed. 0 is the default key, 1 for left, 2 for right, 3 for numeric keypad, 4 for mobile device (virtual keyboard), and 5 for gamepad.
  •  modifiers: A list of space-separated modifier keys such as “Shift”.
  •  repeat (integer) : How many times in a row this key has been pressed.

Note that DOM3 Events discards keypress Events, so keyDown and KeyUp Events can only be simulated as follows:

let textbox = document.getElementById("myTextbox"), 
 event; 
// Create event objects as DOM3 does
if (document.implementation.hasFeature("KeyboardEvents"."3.0")) { 
 event = document.createEvent("KeyboardEvent"); 
 // Initialize the event object
 event.initKeyboardEvent("keydown".true.true.document.defaultView, "a".0."Shift".0); 
} 
// Trigger the event
textbox.dispatchEvent(event);
Copy the code

This example simulates the keyDown event of holding down the Shift key and the A key on the keyboard at the same time. Before using Document.Create Event(“KeyboardEvent”), it is a good idea to check browser support for DOM3 keyboard events. Other browsers will return non-standard KeyboardEvent objects.

Firefox allows you to create keyboard events by passing “KeyEvents” to createEvent(). The returned event object contains a method called initKeyEvent() that takes the following 10 parameters.

  •  type (string) : indicates the event type to be triggered, for example, keyDown.
  •  bubbles (Boolean) : indicates whether an event bubbles. To accurately simulate keyboard events, this should be set to true.
  •  cancelable (Boolean) : indicates whether an event can be cancelled. To accurately simulate keyboard events, this should be set to true.
  •  view (AbstractView) : The view associated with the event, which is basically always document.defaultView.
  •  CTRlKey (Boolean) : Indicates whether Ctrl is pressed. The default is false.
  •  altkey (Boolean) : Indicates whether the Alt key is pressed. The default is false.
  •  Shiftkey (Boolean) : Indicates whether the Shift key is pressed. The default is false.
  •  metakey (Boolean value) : indicates whether the Meta key is pressed. The default is false.
  •  keyCode (integer) : indicates the keyCode for pressing or releasing a key. Used in keyDown and keyUp. The default is 0.
  •  charCode (integer) : indicates the ASCII code of the character corresponding to the pressed key. Used in keyPress. The default is 0.
// Only for Firefox
let textbox = document.getElementById("myTextbox"); 
// Create an event object
let event = document.createEvent("KeyEvents"); 
// Initialize the event object
event.initKeyEvent("keydown".true.true.document.defaultView, false.false.true.false.65.65); 
// Trigger the event
textbox.dispatchEvent(event);
Copy the code

This example simulates the keyDown event of holding down the Shift key and the A key on the keyboard at the same time. Keyup and KeyPress events can also be simulated like this.

For other browsers, you need to create a generic event and assign keyboard-specific information to it, as shown in the following example:

let textbox = document.getElementById("myTextbox"); 
// Create an event object
let event = document.createEvent("Events"); 
// Initialize the event object
event.initEvent(type, bubbles, cancelable); 
event.view = document.defaultView; 
event.altKey = false; 
event.ctrlKey = false; 
event.shiftKey = false; 
event.metaKey = false; 
event.keyCode = 65; 
event.charCode = 65; 
// Trigger the event
textbox.dispatchEvent(event);
Copy the code

The above code creates a generic event, initializes it using the initEvent() method, and then specifies the keyboard event information for it. You must use generic events instead of UI events because UI events do not allow you to add attributes directly to the Event object (except Safari). Emulating an event like this will trigger the keyboard event, but no text will be entered in the text box because it does not accurately emulate the keyboard event.

Simulate other events

Mouse events and keyboard events are the most common mock objects in browsers. However, sometimes you might want to simulate HTML events as well. To model an HTML event, call the createEvent() method and pass in “HTMLEvents”, which is then initialized using the initEvent() method that returns the object:

let event = document.createEvent("HTMLEvents"); 
event.initEvent("focus".true.false); 
target.dispatchEvent(event);
Copy the code

This example simulates firing the Focus event on a given target. Other HTML events can also be simulated like this.

Custom DOM events

DOM3 adds custom event types. Custom events do not trigger native DOM events, but allow developers to define their own. To create a CustomEvent, call createEvent(“CustomEvent”). The returned object contains the initCustomEvent() method, which takes the following four parameters.

  •  type (string) : indicates the event type to be emitted, for example, “myevent”.
  •  bubbles (Boolean) : indicates whether an event bubbles.
  •  cancelable (Boolean) : indicates whether an event can be cancelled.
  •  detail: any value. As the detail property of the Event object.

Custom events can be distributed in the DOM just like any other event, such as:

let div = document.getElementById("myDiv"), event; 
div.addEventListener("myevent".(event) = > { 
 console.log("DIV: " + event.detail); 
}); 
document.addEventListener("myevent".(event) = > { 
 console.log("DOCUMENT: " + event.detail); 
}); 
if (document.implementation.hasFeature("CustomEvents"."3.0")) { 
 event = document.createEvent("CustomEvent"); 
 event.initCustomEvent("myevent".true.false."Hello world!"); 
 div.dispatchEvent(event); 
}
Copy the code

This example creates a bubbling event named “myevent”. The detail property of the Event object is just a simple string,

Both the element and document register event handlers for this event. Because the event is specified to bubble when initialized with initCustomEvent(), the browser is responsible for bubbling the event into the Document.

IE Event Simulation

The process of emulating an event in IE8 and earlier is similar to the DOM approach: create an event object, specify the corresponding information, and then fire using that object. Of course, IE implements each step differently.

First, you create the Event object using the createEventObject() method of the Document object. Unlike DOM, this method takes no parameters and returns a generic Event object. You can then manually specify all the attributes you want the returned object to have. (There is no initialization method.) The final step is to call the fireEvent() method on the event target, which takes two parameters: the name of the event handler and the event object. When fireEvent() is called, the srcElement and Type attributes are automatically assigned to the Event object (all other attributes must be specified manually). This means that all events supported by IE can be simulated in the same way. For example, the following code emulates the click event on a button:

var btn = document.getElementById("myBtn"); 
// Create an event object
var event = document.createEventObject(); 
// initialize the event object
event.screenX = 100; 
event.screenY = 0; 
event.clientX = 0; 
event.clientY = 0; 
event.ctrlKey = false; 
event.altKey = false; 
event.shiftKey = false; 
event.button = 0; 
// Trigger the event
btn.fireEvent("onclick", event);
Copy the code

This example creates the Event object and then initializes it with relevant information. Note that any property can be specified here, including properties not supported by IE8 and earlier. The values of these attributes are not important to the event because they are only used by the event handler

The same approach can be used to simulate keyPress events, as shown in the following example:

var textbox = document.getElementById("myTextbox"); 
// Create an event object
var event = document.createEventObject(); 
// Initialize the event object
event.altKey = false; 
event.ctrlKey = false; 
event.shiftKey = false; 
event.keyCode = 65; 
// Trigger the event
textbox.fireEvent("onkeypress", event);
Copy the code

Since there is no difference between the event objects for mouse events, keyboard events, or other events, any type of event can be triggered using a generic event object. Note that the simulated KeyPress fires, just as the DOM emulated keyboard event does, but there are no characters in the text box.