2-Step Event Registration. A Best Practice. - zjrJS Javascript Library

This information lives on a webpage hosted at the following web address: 'https://www.omegajunior.net/code/zjrjs/'.

Best Practice: how to register events in such a way that they can be removed easily.

A word from a sponsor:


Why
To avoid unwanted behaviour, and possibly free up memory and prevent memory leaks.

When
After your web page or application is done with reacting to certain events, has finished a job, and should be prevented from repeating itself.

When not
Just before unloading / closing your web page or application: the browser's own memory functions should automatically clean up afterwards.

How
Typically, we remove events using the DOM function removeEventListener() (for DOM compliant browsers) or its equivalent detachEvent() (for backwards browsers from stubborn manufacturors). zjrJS.Doc.dE() is a cross-browser wrapper that aims to alleviate the process of having to determine whether a visitor uses a backwards or a complient browser. Other javascript libraries offer their own equivalents.

However, simply knowing to use dE() is half the solution. How do you know which event listeners to remove? Obviously, when an event needs to be executes just once, chances are you already know that, and can remove the event listener from within its own execution code. The zjrJS.Doc.dE() Example Code shows how.

What if you desire an automated system to clear up superfluous event listeners? That system wouldn't know on beforehand which event listeners to remove.

So we have to let it know.

Step 1: register events
For each node that needs to start listening to events that need to be removed later on, we create a new collection property "myEvents". Each item in this collection registers one event listener, with its identifying attributes: event type, and function.

For instance:
<script type="text/javascript" src="zjrJS.version.js" id="zjrJS"></script>
<script type="text/javascript">/* <![CDATA[ */

//define an event listener
function myButtonOnClick(evt){
/* your own click handler code goes here */
}

//assuming your HTML contains a button with id "theClickerButton"
var myButton = zjrJS.Doc.eid("theClickerButton");

//add the events registration
if(typeof(myButton.myEvents)=="undefined"){
myButton.myEvents=[];
}

//register our click handler
myButton.myEvents.push({"type":"click","listener":myButtonOnClick});

//clean up memory
myButton=null;

/* ]]> */</script>


Notice that we just added a reference to the myButtonOnClick function into the button's event registration. Under normal circumstances, this could lead to memory leaks. However, since we do this in order to remove them later on, the leak risk is minimized immediately.

Step 2: add registered events
For each node that needs to start listening to events that need to be removed later on, we loop through its collection property "myEvents". Each item in this collection defines one event listener, with its identifying attributes: event type, and function. We now add these listeners to the node.

For instance:
<script type="text/javascript" src="zjrJS.version.js" id="zjrJS"></script>
<script type="text/javascript">/* <![CDATA[ */

//assuming your HTML contains a button with id "theClickerButton"
var myButton = zjrJS.Doc.eid("theClickerButton");

//add any event listeners, if myEvents exists.
if(typeof(myButton.myEvents)!="undefined"){
for(var i=0, j=myButton.myEvents.length; i<j; i++){
zjrJS.Doc.aE(myButton, myButton.myEvents[ i ].type, myButton.myEvents[ i ].listener);
}
}

//clean up memory
myButton=null;

/* ]]> */</script>


Cleaning up: deleting events
The whole reason for putting in all that extra work, is to allow for an easier clean-up. In the above example, all you have to do is replace aE() with dE(). Lazy copy-pasters can find the clean-up code in this example:

<script type="text/javascript" src="zjrJS.version.js" id="zjrJS"></script>
<script type="text/javascript">/* <![CDATA[ */

//assuming your HTML contains a button with id "theClickerButton"
var myButton = zjrJS.Doc.eid("theClickerButton");

//delete any event listeners, if myEvents exists.
if(typeof(myButton.myEvents)!="undefined"){
for(var i=0, j=myButton.myEvents.length; i<j; i++){
zjrJS.Doc.dE(myButton, myButton.myEvents[ i ].type, myButton.myEvents[ i ].listener);
}
}

//clean up memory
myButton=null;

/* ]]> */</script>



Extra: Event Parameters
Notice that we did not provide any parameters to our event listener function (other than the default "event" parameter, provided by the event system itself). We supply parameters to make functions more generic: write once, use often. With a little adjustment of step 1 we can supply parameters, too:

<script type="text/javascript" src="zjrJS.version.js" id="zjrJS"></script>
<script type="text/javascript">/* <![CDATA[ */

//define an event listener with custom arguments
function myButtonOnClick(evt, arg1, arg2, arg3){
/* your own click handler code goes here,
and you get to define what to do with the passed arguments. */
}

//assuming your HTML contains a button with id "theClickerButton"
var myButton = zjrJS.Doc.eid("theClickerButton");

//add the events registration
if(typeof(myButton.myEvents)=="undefined"){
myButton.myEvents=[];
}

//define the arguments for the event listener
var myArg1 = 1201;
var myArg2 = "bigFont";
var myArg3 = [ 'a','b','c' ];

//register our click handler
myButton.myEvents.push({"type":"click","listener":new Function("myButtonOnClick(" + myArg1 + ", '" + myArg2 + "', " + myArg3 + ")"});

//clean up memory
myButton=myArg1=myArg2=myArg3=null;

/* ]]> */</script>


This way instead of a function reference, we have supplied a newly created function, in which we call the predefined function, passing specific parameters. Note that this method prevents memory leaks. Also note that the 2nd parameter is a string, and since the new function is created using a string itself, we must supply additional quotes around the parameter, otherwise it is passed as a variable name which may or may not be defined, leading to all kinds of unexpected behaviour.

Download:

Download zjrJS’ latest stable build (20141202t2322) minified (7.1KB).

Download zjrJS’ latest stable build (20141202t2322) (11.9KB).

Clicky Analytics