Archive for the ‘JavaScript’ Category

JS/DOM AHA Moment: getElementsByTagName Collection is Always Up to Date According to the Current DOM Tree

Tuesday, September 7th, 2010

Today I found out something interesting about getElementsByTagName. This method is quite popular, it returns all elements of a requested tag under a specific parent element.

The type which is returned out of a getElementsByTagName call is NodeList, which is not exactly an Array, but is enumerable and has the length property.

When you call document.getElementsByTagName("div") you get a NodeList of all div tags, but it seems that every time you use this variable, it shows an up to date list.

Meaning – if you have a NodeList instance of all divs on the document and then you dynamically add more divs to the document, this instance will contain all the new divs as well. Same for removed divs.

Test Case:

<div></div>
<div></div>
<div></div>
<div></div>
var divs=document.getElementsByTagName("div");
console.log("divs.length="+divs.length); // 4
document.body.appendChild(document.createElement("div"));
document.body.appendChild(document.createElement("div"));
document.body.appendChild(document.createElement("div"));
console.log("divs.length="+divs.length); // 7
document.body.removeChild(divs[0]);
console.log("divs.length="+divs.length); // 6

How to Maintain the Original Collection?

Simply, by creating a new Array out of the NodeList‘s contents:

var divs=document.getElementsByTagName("div");
divs=Array.prototype.slice.call(divs);
// the above is a shortcut to:
// var tmp=[];
// for (var i=0,l=divs.length;i<l;i++) tmp.push(divs[i]);
// divs=tmp;

MooTools, jQuery, Prototype etc. do not return a NodeList out of their DOM selector engines. They return an Array instance, so no need to worry about this.

This also applies to getElementsByClassName and getElementsByName, but not to querySelectorAll.

I investigated a bit inside the WebKit’s source and it seems like that the getElementsByTagName / getElementsByClassName / getElementsByName methods return a DynamicNodeList while the querySelectorAll method returns StaticNodeList.

JavaScript ‘this’ Keyword

Monday, August 2nd, 2010

Definition of the JavaScript this Keyword – The object which called a function.

The JavaScript this keyword is used within any function scope or the global scope, and in each scope it receives a different value. The value of this inside a function, effectively depends on the object which called it.

What exactly is scope? Every function gets its own space in order to define variables and properties. When we want to access a certain variable, we must do it through the space in which it was defined. In JavaScript, when we haven’t defined a function, everything happens in global scope.

Code

var o={}; // declaring new object
o.name="moon";
o.method=function () {
	alert(this.name);
};
o.method();

We’ve defined a new object named o and a method which displays this.name. But where does this come from?

By calling o.method(), we’ll get the value "moon". The process that’s taking place is the following:

  1. We create a variable of type string with the value "moon" and assign it to the name property of object o.
  2. We create a function with certain content and assign it as a method of o.
  3. We call the function so that o is the object that calls it.
  4. The function is called when this is actually a reference to o. We get o.name which is "moon".

It is important to note that a function is a variable in every sense. It contains expressions which we can call later on. Having this function assigned to o’s property “method” doesn’t connects the this variable inside it to o. The object doesn’t know about the contents of this function. The actual assignment of this only happens when the function is called, when this is translated as the object that called it, hence, o.

From here, we can make it a slightly more sophisticated:

var o={}; // declaring new object
o.name="moon";
o.method=function () {
	alert(this.name);
};
var x={};
x.name="sun";
x.method=o.method;
x.method();

We’ve declared a new object named x with a name property set to "sun".

We’ve put a reference to the method we defined in o into x.method as well. When calling x.method, we won’t get, god forbid, "moon", because there is no actual assignment to o. We get "sun" because x is the object that called the function, and the value of this is set to the object which called the function.

We can say that every JavaScript function has a hidden argument named this. Furthermore, with each call to the function, the argument is passed behind the scenes with the pointer to the object which called the function.

function f() { alert(this.name); }

Becomes:

function f(this) { alert(this.name); }

Meaning:

var o={}; // declaring new object
o.name="moon";
o.method=function (this) {
	alert(this.name);
};
o.method(o);
var x={};
x.name="sun";
x.method=o.method;
x.method(x);

In C++, the compiler adds an argument named this to every method in a class, which is an object of the class type, and effectively every call to a method becomes a call to the method together with that variable (please ignore C++ wrong syntax):

class Foo {
	string name="foo";
	string GetName() { return this.name; }
}
Foo o=new Foo();
string s=o.GetName();

Actually becomes something like:

class Foo {
	string name="foo";
	string GetName(Foo this) { return this.name; }
}
Foo o=new Foo();
string s=o.GetName(o);

More on this in C++ on Wikipedia

Changing the Value of this

It is possible to change the value of this, not by assignment (you cannot assign a value to this), but rather by calling a function via another object, as we did with x – defining the function as a property of an object and calling the function through it.

JavaScript gives us the tools to call a function with a different this without too much effort, with the help of the methods call and apply. These two methods are quite similar: they are methods of a function (defined in Function.prototype) that take an object that should be the this and arguments, and call the function itself with these arguments. The difference is in how the arguments are passed – call takes regular arguments (like params in C#), and apply takes the arguments as an array.

We’ll change the former code to work with call:

var o={}; // declaring new object
o.name="moon";
o.method=function () {
	alert(this.name);
};
var x={};
x.name="sun";
o.method.call(x);

What happens behind the scenes, sort of:

x.temporaryMethod=o.method;
x.temporaryMethod();

this and new Operators

A JavaScript function is not just a group of statements for later use, it also serves as a class; every function is also a class. We can create instances of a class.

function Foo(name) {
	this.name=name;
}

This function, like all functions, is also a class; we create an instance of it thus:

var instance=new Foo("bar");
alert(instance.name); // "bar"

What is actually happening behind the scenes? When we call new Foo("bar"):

var instance={}; // declaring new, empty object
Foo.call(instance,name); // calling the constructor with the new object as this, and the argument "name"

Calling the constructor will call the expression:

this.name=name; // this is the "instance" object. assign name as the sent argument

The compiler creates a new, empty object and activates the class’ constructor with call. This way, this will be a reference to the newly created object. The constructor calls the expressions and the references to this that it contains, and then new object is returned.

this and prototype

The prototype property is a class property, and whichever value we assign it will be available to all instances of the class. When accessing an object property, the existence of the property in the object will be checked first and, if not found, will be checked in the prototype of the class to which the object belongs (with Object being the basic class).

In JavaScript there are a number of pre-defined classes:

Object
Function
String
Number
Array
Date
Error
RegExp

We can extend any class with our own functions, and so each instance of the class will contain our extension.

C# contains a feature called extension method which extends the functionality of built-in classes such as int and string. This feature is also found in Ruby.

String.prototype.trim=function () {
	return this.replace(/^\s+|\s+$/g,"");
};
var s="    x        ";
alert("<"+s+">");
alert("<"+s.trim()+">");

After extending the prototype of the class String with a property named trim which is a function that trims blank spaces from the beginning and end of the string, we can access this method from any string. this in the function will contain the value of the string, because the string variable is the object that called the function.

this and the DOM

<div id="div" onclick="alert(this.innerHTML);">div</div>

The DOM knows to interpret the code thus:

div.onclick=function () {
	alert(this.innerHTML);
};

From here it looks familiar; onclick is defined as a property whose value is a function with use of this.

The browser will apply a “listener” for mouse clicks. Once a click is received, it checks whether its location is on this div. If so, it checks whether the div has an onclick attribute, and then just calls the function like this:

div.onclick(event);

Thus, this contains a reference to the div.

Summary

this is a wide world in JavaScript. Comprehensive understanding of it will allow us to write shorter, more efficient and advanced code.

MooTools: Track Class Instances With The TrackInstances Mutator

Monday, July 19th, 2010

(Script also on GitHub and MooTools Forge)

It’s great to track all of your classes’ instances for debugging purposes or mass altering.

Huh? Mass Altering?

  • For instances of some Audio/Video player classes: stop/pause all players
  • For instances of positioned elements: recalculate position of all elements upon window resize
  • Call hide method of all instances of a class upon an event

I needed it more than once and I used to track instances from within any class I needed it:

var MyClass=new Class({
	initialize:function () {
		MyClass.instances.push(this);
	},
	recalcPosition:function () {
		// something that recalculates position or any other task that should happen on all instances
	}
});
MyClass.instances=[];
// from another code:
MyClass.instances.each(function (instance) {
	instance.recalcPosition();
});

However, I got too many classes with this behavior. I ended up writing a MooTools Class Mutator to automate the process:

/*
---
script: Class.Mutators.TrackInstances.js
description: Allows a class to track its instances by having instances array as a class property
license: MIT-style license
authors:
- Elad Ossadon ( http://devign.me | http://twitter.com/elado )
requires:
- core:1.2.4
provides: [Class.Mutators.TrackInstances]
...
*/
Class.Mutators.TrackInstances=function (allow) {
	if (!allow) return;
	// save current initialize method
	var oldInit=this.prototype.initialize;
	var klass=this;
	// overwrite initialize method
	klass.prototype.initialize=function () {
		(klass.instances=klass.instances || []).push(this);
		oldInit.apply(this,arguments);
	};
};

Usage:

var MyClass=new Class({
	initialize:function () {
	},
	TrackInstances:true,
	recalcPosition:function () {
		// something that recalculates position or any other task that should happen on all instances
	}
});
var x=new MyClass();
var y=new MyClass();
MyClass.instances; // [x, y]
MyClass.instances.length; // 2
// from another code:
window.addEvent("resize",function () {
	MyClass.instances.each(function (instance) {
		instance.recalcPosition();
	});
});

The only constraint is that the “initialize” declaration in an object should be prior to the TrackInstances:true. That’s because mutators are called in the same loop as all other methods, and if the loop didn’t get to a certain method, then the mutator won’t know it. According to these tickets, the MooTools team won’t fix it anytime soon.

Bug: Chrome’s for..in Loop Messes the Order of the Indexes

Tuesday, July 13th, 2010

Yes. I know I should never rely on for..in loop order, but when it comes to inconsistency of browsers it can be annoying.

I found it out when I wanted to fetch the first key of an object, but got different results on chrome (incorrect result) and other browsers.

Code:

console.log("wordsAsKeys");
var wordsAsKeys={ words:"value",as:"value",keys:"value" };
for (var k in wordsAsKeys) console.log(k);
console.log("numbersAsKeys");
var numbersAsKeys={ "3":"value","2":"value","1":"value" };
for (var k in numbersAsKeys) console.log(k);
console.log("mixedKeys");
var mixedKeys={ "3":"value",words:"value","1":"value" };
for (var k in mixedKeys) console.log(k);

The Result:

Firefox / Safari / IE:

wordsAsKeys
	words
	as
	keys
numbersAsKeys
	3
	2
	1
mixedKeys
	3
	words
	1

Chrome:

wordsAsKeys
	words
	as
	keys
numbersAsKeys
	1
	2
	3
mixedKeys
	1
	3
	words

As you can see, the second and third loops enumerate on an object with numbers as indexes. What Google Chrome seem to do is treat the object as an Array, which outputs the numeric indexes first, in ascending order.

To me, it looks like a bug that may create inconsistency. Google & Chromium team – please fix it :)

Asynchronous Waiting for JavaScript Procedures

Monday, July 5th, 2010

The Web is asynchronous. Many different types of components make up a system, and each one loads separately. Each component has a different way of loading, and a different way of notifying that it has loaded. Sometimes, it isn’t enough to have the component fully loaded, since affects other components on the page, and we may not always have a way of knowing about it.

Common object types are images, CSS files, JavaScript files, and XMLHTTP Requests. Each has an event to notify us that it has loaded, but having loaded does not mean that the component has begun to affect our page.

For example:

  • Load a CSS file in runtime and appending it to the page does not mean it will immediately affect its elements.
  • Receiving HTML code via an XMLHTTP request and appending it to the page does not mean that it will all be immediately rendered and that its components are accessible.
  • Loading a JavaScript file does not mean all of the objects it contains are ready for use.

For these extreme cases, I’ve written an elementary function which waits for a certain procedure to complete and only then executes further code.

/// $waitUntil
///		waits until a certain function returns true and then executes a code. checks the function periodically
/// parameters
///		check - a function that should return false or true
///		onComplete - a function to execute when the check function returns true
///		delay - time in milliseconds, specifies the time period between each check. default value is 100
///		timeout - time in milliseconds, specifies how long to wait and check the check function before giving up
function $waitUntil(check,onComplete,delay,timeout) {
	// if the check returns true, execute onComplete immediately
	if (check()) {
		onComplete();
		return;
	}
	if (!delay) delay=100;
	var timeoutPointer;
	var intervalPointer=setInterval(function () {
		if (!check()) return; // if check didn't return true, means we need another check in the next interval
		// if the check returned true, means we're done here. clear the interval and the timeout and execute onComplete
		clearInterval(intervalPointer);
		if (timeoutPointer) clearTimeout(timeoutPointer);
		onComplete();
	},delay);
	// if after timeout milliseconds function doesn't return true, abort
	if (timeout) timeoutPointer=setTimeout(function () {
		clearInterval(intervalPointer);
	},timeout);
}

POC

var globalVariable=0;
setTimeout(function () { globalVariable=1; },2000);
$waitUntil(
	function () {
		console.log("checking globalVariable="+globalVariable);
		return globalVariable==1;
	},
	function () {
		alert("done!");
	}
);

In the example, we can see there is a variable named globalVariable initialized to 0, and that is manually set to 1 after 2 seconds. The function $waitUntil takes two functions. One will return true / false according to tests it will carry out, and the other will execute only after the first function returns true.

Examples of more common uses:

ajax("url.htm",function (source) {
	element.innerHTML=source;
	$waitUntil(
		function () { return document.getElementById("some-id-in-source")!=null; },
		function () { /* source is rendered and #some-id-in-source is available */ }
	);
});
loadCss("file.css",function () {
	$waitUntil(
		// element is an element whose height affected by a rule in file.css
		function () { return element.offsetHeight>0; },
		function () { /* css is applied on this element */ }
	);
});
loadJs("file.js",function () {
	$waitUntil(
		function () { return typeof(SomeTypeInFileJs)!="undefined"; },
		function () { /* SomeTypeInFileJs is ready to use */ }
	);
});

The ajax, loadCSS and loadJS functions asynchronously load files and notify that they have loaded. Their implementation can be found in any self-respecting JavaScript library.