JavaScript: Attaching/Detaching Event, OnLoad vs. DOMContentLoaded, Manipulating DOM and CSS

Event Handling, Attaching and Detaching Events

3 phases of event dispatch:

  • Capturing – going down the DOM tree until it reaches the target element
  • Target
  • Bubbling – going back up the DOM tree from the target element

 

// standard way of accessing the Event object
var eventHandlerDOM = function(evt) {
alert(evt.type); //displays click
}

// starting IE 9 you can use the standard way
var eventHandlerIE8Older = function() {
var evt = window.event;
alert(evt.type);
}

// cross-browser way
var eventHandlerCB = function(evt) {
if (!evt) evt = window.event;
alert(evt.type);
}

window.onload = function() {
var btn = document.getElementById("btn");

// 2 ways of attaching an event
btn.onclick = eventHandlerCB;
// or ...
if (btn.addEventListener) {
btn.addEventListener("click", eventHandlerCB, false);
}
else {
// starting IE 9, you can use the standard way using
// addEventListener
btn.attachEvent("onclick", eventHandlerCB);
}

alert("events attached");
alert("now detaching events...");
detachEvents();
alert("events detached");
}

// detaching events
var detachEvents = function() {
if (btn.removeEventListener) {
btn.removeEventListener("click", eventHandlerCB, false);
}
else {
// starting IE 9, you can use the standard way using
// removeEventListener
btn.detachEvent("onclick", eventHandlerCB);
}
}

 

window.onload Event vs. document.DOMContentLoaded event

The problem with running script on window.onload event is that if you have a rather large image to download, your script will run only after the image is downloaded.  window.onload event is triggered after everything, including external stylesheets, javascript files and images, has been downloaded.  An alternative, and usually a best practice, is to run your script on document.DOMContentLoaded event.

var readyFunc = function() {
var stat = document.getElementById("statDOMReady");
stat.innerHTML = "DOM is ready!!!";
}

document.addEventListener("DOMContentLoaded", readyFunc, false);

 

Note that document.DOMContentLoaded event is an HTML5 specification.  Also if your script needs to work with CSS, you need to put all your external stylesheets in the header and all your external scripts in the footer so all the styles will be loaded before your scripts are run.

 

Manipulating DOM

A sample of what you can do for manipulating DOM:

  • document.createElement() – create an Element node
  • document.createTextNode() –  create a Text node
  • element.cloneNode() – create a copy of a node and its attributes with option to include the descendants as well
  • element.appendChild() – add a new child node as the last child node
  • element.insertBefore() – insert a new child node before a specified child node
  • element.removeChild() – remove a child node
  • element.replaceChild() – replace a child node
  • document.createDocumentFragment() – create an imaginary Node object; useful for adding content to your document, or extracting parts of your document and modifying it and inserting it back

 

Manipulating CSS

2 ways to import external stylesheets:

<!-- using <style> tag and @import -->
<style type="text/css">
@import "myStylesheet.css";
</style>

<!-- using <link> tag and rel="stylesheet" -->
<link type="text/css" href="myStylesheet.css" rel="stylesheet" />


 

Persistent, preferred, and alternate stylesheets:

<!-- persistent stylesheet -->
<link type="text/css" href="main.css" rel="stylesheet" />

<!-- preferred stylesheet -->
<link type="text/css" href="dflt.css" rel="stylesheet" title="Default" />

<!-- alternate stylesheet -->
<link type="text/css" href="alt1.css" rel="alternate stylesheet"
title="Alternate 1" />

 

element.style, window.getComputedSyle(), element.currentStyle, elements positioning properties:

var p1 = document.getElementById("p1");
p1.style.fontSize = "20px";

// standard way of getting the computed style
var computedStyle = window.getComputedStyle(p1, null);
alert(computedStyle.fontSize); // displays 20px

// IE way of getting the computed syle
// starting from IE9, you can use the standard way
var computedStyle2 = p1.currentStyle;
alert(computedStyle2.fontSize); // displays 20px

// cross-browser code for getting the computed style
if ( !("getComputedStyle" in window) ) {
alert("getComputedStyle does not exist")
window.getComputedStyle = function(element) {
return element.currentStyle;
}
}

// positioning properties of an element you can use
alert(p1.offsetTop); // displays 130
alert(p1.offsetLeft); // displays 8
alert(p1.offsetHeight); // displays 23
alert(p1.offsetWidth); // displays 911
// below will display 0, because it's parent is the body tag
alert(p1.offsetParent.offsetTop);
var body = document.getElementsByTagName("body")[0];
alert(body.offsetTop); // displays 0

JavaScript: RegExp, Error, DOM, and Event

This post is a follow-up to my previous posts, Object Oriented Programming In Javascript and JavaScript Array and How It’s Different from Object.  In this post I will touch on regular expressions, error handling, basic DOM scripting, image preloading, and timing events.

 

Regular Expressions

// creating patterns
var pattern1 = new RegExp("Rodan"); // using constructor
var pattern2 = /Rodan/; // using literal enclosed in "/"

// testing strings against the patterns
alert(pattern1.test("Rodan Sotto")); // displays true
alert(pattern2.test("John Doe")); // displays false

// using String methods search, match, and replace with regular expressions
var string1 = "Rodan Sotto";
var string2 = "John Doe";

// below will display 6, the starting position of the substring found
alert(string1.search(/Sotto/));
// below will display -1, which means no substring found
alert(string2.search(/Sotto/));

alert(string1.match(/Sotto/)); // displays an array ["Sotto"]
alert(string2.match(/Sotto/)); // displays null

// below will display "Rodan Nolastname"
alert(string1.replace(/Sotto/, "Nolastname"));
// below will display "John Doe"
alert(string2.replace(/Sotto/, "Nolastname"));

See JavaScript RegExp Object for more information.

 

Error Handling

var testErrorHandling = function () {
try {
alert("Throwing an error inside the try block...");
throw(new Error("You've got an error!!!"));
}
catch (error) {
alert(error.message);
}
finally {
alert("Error or not, finally will get executed");
}
};

 

Basic DOM Scripting

W3Schools.com has a very good beginner resource on HTML DOM.

  • DOM stands for Document Object Model.  Everything in DOM is a node.  A Node can be any of the following:
    • Document Node
    • Element Node
    • Attribute Node
    • Text Node
    • Comment Node
  • Document Object, which represents your web page, is the first object you will need to access the DOM.  It is the root node of the HTML document and the owner of all other nodes.  See Document Object Properties and Methods.
  • Element Object, which represents an HTML element, is the second object you will need to access the DOM.  Most commonly a call to document.getElementById() method will return an element object.  An element object is also an element node.  It can have child nodes such as element nodes, attribute nodes, text nodes, or comment nodes.  See Element Object Properties and Methods.
  • An Element Object can be one of the following:
    • Anchor Object
    • Image Object
    • Button Object
    • etc., each of which can have it’s own set of properties and method.
  • And lastly, the Attr Object which represents an HTML attribute and always belongs to an HTML element.  See Attr Object Properties and Methods.
  • Events.  See HTML DOM Events.  It lists the different events such as:
    • Mouse Events
    • Keyboard Events
    • Frame/Object Events
    • Form Events
    • It also lists the properties and methods of event objects that event handlers have access to such as:
      • Event Object
      • EventTarget Object
      • EventListener Object
      • DocumentEvent Object
      • MouseEvent Object
      • KeyboardEvent Object

 

Default Action Cancellation

var event_handler = function(evt) {
if (!evt) evt = window.event; // for IE

// ...

// cancel default action
if (evt.preventDefault) {
evt.preventDefault(); // for most browsers
}
else {
evt.returnValue = false; // for IE
}
}

window.onload = function() {
var link = document.getElementById("link");
link.onclick = event_handler;
}

 

Image Preloading

var image = new Image();
image.src = "image.jpg";
// or
image.src = link.href;

 

Timing Events

JavaScript also provides for executing code at specific time-intervals using setInterval() and setTimeout() methods.  See JavaScript Timing Events.  

.NET: Regular Expression Testers and Quick Reference Guides

I talked briefly about regular expressions in .NET Framework here, how you can test for a match and how to extract or replace a matched group, or matched substring if you may.  The examples there used a named group to extract a matched group (e.g. ?<proto>?<port>, etc…).  It’s worth mentioning that you can also extract a matched group using $ (e.g. $0, $1, $2, … $n, where $0 is the whole string that was matched, $1 is the first matched group, $2, the second matched group, and so on…).

Also it would be nice if you have a tool you can use to test out your regular expressions instead of testing it out in your code, compiling it, and running it.  Well there are tools out there available for you but the ones I liked the most are online tools and are listed below.

Online Regular Expression Tester

  • A Better .NET Regular Expression Tester – it’s user interface is not fancy but it does the job well.
  • Regex Storm .Net – this one is a much better user interface and does the job too.
  • RegExr – an online tool to learn, build, & test. It has a cheatsheet and a very good sample text to test your regex.

Online Quick Reference Guides

 

So what is Closure?

I’m not talking about closure in a relationship :).  I am talking about closure in programming.  I first encountered closure in JavaScript the hard way.  I was debugging for days the following code as to why it was not behaving the way I expect it to be.

var setRowsOnclickEvent = function () {
    var table = $("tableList");
    
    for (var i = 1; i < table.rows.length; i++) {
        var row = table.rows[i];
        
        row.onclick = function () {
            var table = $("tableList");
            
            for (var j = 1; j < table.rows.length; j++) {
                var row = table.rows[j];
                
                if (j == i) {
                    row.className = "selectRow";
                }
                else {
                    row.className = "unselectRow";
                }
            }
        }
    }
}

 

 

The JavaScript function above is setting the onclick event of all the rows in the table.  It iterates through each row in the table and assigns an inline function, an event handler, to the row’s onclick event.  Note that the event handler, or the inner function, uses the loop variable i of the outer function, containing the row number, so that whenever the row is clicked it get’s highlighted.

My mistake was to think that the value of i is passed on to the event handler like a function parameter and placed on the stack.  Don’t make that mistake. The value of i actually closes on you, I mean on the event handler, or the inner function.  So variable i is a closure variable, while the inner function is a closure function.

Again don’t make the mistake that assigning the event handler to the onclick event actually calls the event handler.  It’s an event handler so it can be called even after the outer function has been out of scope.

So you ask, what happens to the value of i then? Well for closure situations like this, the compiler creates a special context object containing the closure variables so that closure functions, when they get executed, will have access to these variables even if the function that the closure variables are declared in are already out of scope.

In the above JavaScript code, what’s happening is whenever any row is clicked, the last row always gets highlighted, so which is to say, the variable i always has the value of the last row number.  The reason is because, again, even if the closure function, or the event handler in our example, is inside a loop that iterates through each row and incrementing the closure variable i, when the loop exits, the closure variable i will contain the last value in the loop which is the last row number and that is the value that the event handler will get when it gets executed at a later time.  So the value of the closure variable i will be the value at runtime and not at capture time, or in our example, not at the time when the event handler is declared and assigned.

Below is my solution to the JavaScript code above.  The solution is to wrap the event handler in another function where I pass in i as a function parameter, thereby forcing the compiler to capture the value of i as it increments itself inside the loop.

var setRowsOnclickEvent = function () {
    var table = $("tableList");
    
    for (var i = 1; i < table.rows.length; i++) {
        var row = table.rows[i];
        
        row.onclick = (function (i) {
            return function () {
                var table = $("tableList");
                
                for (var j = 1; j < table.rows.length; j++) {
                    var row = table.rows[j];
                    
                    if (j == i) {
                        row.className = "selectRow";
                    }
                    else {
                        row.className = "unselectRow";
                    }
                }
            }           
        })(i);
    }
}

 

 

EDIT:  The code above works by making the function (outer function) that wraps the event handler (inner function) as an IIFE (Immediately Invoked Function Expressions).  This creates a scope object for the outer function (remember Javascript scoping stops at the function level while C# continues at the block level) containing the current value of i in the loop which is retained even after the outer function has returned.  Then each row’s onclick event will end up having an inner function with access to a separate outer function’s scope object, each containing a different value of i.

It’s not only the JavaScript language that has closure.  Other languages have it as well, like C#.  I encountered closure in C# the same way I did in JavaScript.  I was declaring/passing in a lambda expression to a function call inside a foreach loop, that references the foreach variable.  And since C# has a slightly different handling of closure than JavaScript, the solution is different.  In C#, one just need to assign the foreach or loop variable to a temporary variable and reference the temporary variable instead.

public void SomeFunction()
{
    // ...
    foreach (Filter filter in filters)
    {
        // have to assign filter value to temp variable in the loop
        //  because the lambda expression we're using here is in a closure
        string filterVal = filter.Value;
        switch (filter.Field)
        {
            case "Location Name":
                locs = locs.Where(l => l.LocationName.Contains(filterVal));
                break;
            // ...
        }
    }
    // ...
}

 

 

It’s not easy to wrap your mind around closure, at least for me.  Closures in C# vs JavaScript – Same But Different really explains it to me clearly, from a really really technical point of view, as in how the compiler handles closure both in C# and JavaScript.  Don’t worry it’s not a long read but one needs to really read through the code. I’ve got other links for further reading if need be:

I can’t guarantee you to become a closure relationship expert, but after going through the readings and links above,  I can guarantee you to become a closure programming expert ;).