Wednesday, August 27, 2008

JavaScript DOM property listeners

Here's a scenario.... Say we have an image on an HTML page that can be dragged and dropped anywhere within the browser window. Now, we need to perform some operations such as displaying the current location of the image as soon as the position of the image changes. The most common way of changing the position of an element in JavaScript is by modifying its style attribute's "top" and "left" properties. Now, we can detect such modifications in most current browsers by using functions available on the DOM elements. The syntax and working of these events is very different between the various browsers but suffice for most situations.

Basically, IE supports the "onpropertychange" event on DOM elements while FF and Opera support the "DOMAttrModified" event. Here's some sample code that's been tested in IE7, FF2 and Opera 9.5, that illustrates how these events can be used. Safari has no equivalent event in its repertoire, so the code below doesn't work in it at the moment.

function changeDivAttrs(){
var div = $("testDiv");
div.style.borderColor = "green";
}
function setWatches(){
var div = $("testDiv");
if(browser.IE)
div.setAttribute("onpropertychange",handleChange);
else
div.addEventListener("DOMAttrModified",
handleChange,false);
}
function handleChange(e){
if(browser.IE) {
//comment this line to view all attributes affected
if(event.propertyName == "style.borderColor")
showMessage(event.srcElement,
event.srcElement.id,
event.propertyName);
}
else //Firefox and Opera
showMessage(e.target,e.target.id,e.attrName);
}
function showMessage(element,id,propertyName){
alert(element + ":" + id + ":" + propertyName);
}
//HTML Section
< body onload="setWatches()" >
< div id="testDiv"
style="border-color:red;
border-style:solid;
border-width:2px" >
Test DIV Container
< /div >
< input type="button" onclick="changeDivAttrs()"
value="Change" />
< /body >
FF and Opera return the property name as just "style" while IE returns "style.borderColor" (kudos to the IE developers, for once!!!). Also, FF and Opera really watch for just changes i.e. the event is fired only when the previous value of the attribute is different from the new value. IE, on the other hand, fires every time the property is set or reset, even if the value hasn't really been modified. Additionally, comment the check in the function "handleChange" to see that the properties "borderTopColor","borderRightColor","borderBottomColor" and "borderLeftColor" are also getting affected.

Cheers.

Tuesday, August 26, 2008

Comet in ASP.Net

Comet, in general, is the technique of enabling server-side push instead of having client-side pull for refreshing information in the user's interface. The latter involves regular pinging of the server from the client to check for any changes that may have occurred on the server and send the corresponding information to update the user's UI accordingly. In applying Comet, one can notify the server-side changes to the client without the need of the constant pinging. Typical scenarios where Comet appears to have a relevant niche involve browser based chat applications, online collaboration and editing systems (such as GoogleDocs) and browser based web OSs.

Comet ties client-side JavaScript tightly with the server-side by having a long-running HTTP connection through which the server can push updates to the client as and when they occur. One technique that has worked well for me has been opening having one connection open in a hidden iframe on the page that's long-running. Another connection that sends information from the client up to the server is also established whenever corresponding user interaction takes place. This second connection opens and closes just like a regular HTTP request and can be performed using AJAX.

In an great example by Dave Ward at Encosia, he provides a very basic example of how the server can push data down to the client by using sleep states on the server. He performs a callback on the client by writing out pieces of JavaScript to the long-running connection, thus making it run within the iframe and, since its in the same domain, can call methods in the parent window to perform updates in the browser window. This and a number of other articles I read up elsewhere prompted me to try and use this technique in a real world application. The application I felt I could quickly demonstrate Comet was a chat application.

The image below shows the chat application in operation in two different browsers on the same machine. The example was written in Visual Web Developer 2008 using the Microsoft Dot Net Framework 3.5. The code works in FF2 and Opera 9.5 so far. Not too many optimizations have been performed as the goal of this was to just demonstrate a way in which Comet could work without having to switch to any "dedicated Comet" servers. The same principles could be applied in Java too where the two connections could be established with two separate servlets to achieve the same results. In case anyone would like to view the source code for this, email me at avinpr@yahoo.com.

Cheers.

Monday, August 18, 2008

Namespacing in JavaScript

Namespacing, in general, allows one to provide a context for the content to be declared in one's code. It helps resolve same name ambiguities by having variables, functions and even objects residing in different namespaces. JavaScript, unlike Java, does not have the "namespace" keyword as part of its standard list.

Now, there are different ways of defining namespaces in JS. The root namespace's name should be as unique as possible and should generally be having application scope, but there is no restriction present and can be named according to one's preferences. Before delving into more details, I think an example will give all a better idea on this. So, here goes:

var MyNameSpace = {};

MyNameSpace.MyClass = function(str){
this.name = str;
this.alertClassName = function(){ alert(this.name); }
}

//usage
with(MyNameSpace){
var myObject = new MyClass("Class1");
myObject.alertClassName();
}

Here, an object called "MyNameSpace" is created in the global(window) scope of things. Now, one must ensure that only one root namespace exists. There are a variety of ways one can do so but I shall not delve into those methods at this time.

The class declaration basically creates a property of the "MyNameSpace" object and hence declaration of any object created from this class must use the root namespace before an instance can be created. This also encapsulates the class("MyClass") and ties it to the root namespace and hence prevents name conflicts with any other included javascript classes or variables that may also have the same name "MyClass".

There exists other ways in which namespaces can be declared but I feel the example above is easiest to understand and implement. In case you feel otherwise, I'd like to know why.

Cheers.

Tuesday, August 5, 2008

JavaScript String Reversal

I was just surfing through some snippets when I came across some code for string reversal in JavaScript. Often people post code without having tested it themselves, which is just wrong!!! Here's a snippet I saw when going through one of these posts:
//NOTE: Non-functional JS code for string reversal
for (var i = 0; i < middle; i++) {
var temp = thestring[i];
thestring[i] = thestring[last-i];
thestring[last-i] = temp;
}
Now, one basic thing most people don't realize is that in JavaScript, individual characters of a string cannot be altered by using just the indexes. The above piece of code leaves the string unaltered not throwing any exceptions. The assignment to the individual characters is just ignored and "thestring" remains unchanged. Tested this in FF2, IE7 and Opera 9.51 and they all yielded the same result.

Since string reversal is a very common operation, it could be added to the String prototype if it makes sense. The following snippet does the job quite well, using two very useful functions available to arrays, reverse and join.
/* Full credit to Shachi Bista
http://www.bytemycode.com/snippets/snippet/400 */
String.prototype.reverse = function(){
splitext = this.split("");
revertext = splitext.reverse();
reversed = revertext.join("");
return reversed;
}
Now I prefer to always explicitly declare variables before using them. In this case, "splitext" and "revertext" become global members and could affect other variables that have the same name. Thus, to keep it localized, always explicitly declare the variable within the functions unless you want it to be available everywhere. So here's my revised version of it:
String.prototype.reverse = function(){
var splitext = this.split("");
var revertext = splitext.reverse();
return revertext.join("");
}
If you have a different approach or find a bug in my logic, do let me know.

Cheers.

Monday, August 4, 2008

JavaScript type checking and "isArray" function

Now javascript is a loosely typed language. What do I mean by that? It means just one "type" of variable exists in javascript represented by "var". Even declaring objects of your own type is done in the same manner. This is unlike Java or C++ in which the basic data types are declared as:
int a = 10; string b = "hello world"; ...

In JS, all variables (and even functions) can be referenced by using simple "var" declarations. For example, var a = 10; var b = "hello world";...

Now this causes a problem in checking the type of any object passed into a function. For example, when a function is expecting a string but the callee passes in an integer as a parameter, the function may falter. To do this, checking the type of the variable being passed in can help avoid unexpected errors and continue program execution even when an error occurs or using appropriate error handling capabilities (which I shall speak of on another post).

One of the most common operations when dealing with JSON(JavaScript Object Notation) during AJAX communications is the need to check if the returned object is a single object or an array of similar objects. For doing this there are a variety approaches:
function isArray(obj){
return obj && (obj.constructor == Array);
//return obj && (obj instanceof Array || typeof obj == "array"); //DOJO toolkit approach
/* if(obj.constructor.toString().indexOf("Array") == -1)
return false;
else
return true;*/
/* if(!obj || typeof obj == "string")
return false;
return obj.length != null; */ //had to use this for Nokia S60 browsers since constructor wasn't recognized
}

I found the first solution very concise and works on most modern browsers. A useful piece of information is that the "===" (triple equals to) checks for type equality compared to the "==" which checks only the values, which can be used in various other situations.

I'll leave this topic open to debate. There's no one perfect solution on every platform for this, primarily due to the differences in implementation in the various browsers.

Cheers.