Introduction
If you have to implement a Web application that intensively uses JavaScript for some client side effects or some AJAX like features, and if you have to support more than Microsoft Internet Explorer 6.0, then you might find here a not widely known trick to make your programming easier by implementing a small compatibility layer.
Both browsers, Microsoft Internet Explorer and Mozilla/FireFox, do have a lot of functionalities as defined by the standards HTML, CSS and JavaScript, and some of them are really useful for implementing some functionality on the client. Because there are some extensibility features available on these different platforms, it is possible to extend one browser with methods and properties that are not available in the other one out of the box.
JavaScript Prototypes
The JavaScript language interpreters included in the browser platforms offer almost identical functionalities. There is no real need to use this feature to bring some missing functionalities to one of the platforms, but it is an interesting feature anyway and it gives you the chance of extending both platforms.
The object types of JavaScript, better called intrinsic objects of JavaScript (String
, Number
, Array
, �) that are part of the JavaScript language have an extensibility mechanism by using the available prototype
property.
By attaching new functions to the prototype
, you make this method available to all objects that derive from that type.
The following sample adds a trim()
method to all string objects that eliminates all leading and trailing blanks and returns a new string with the remaining characters (if any):
String.prototype.trim = function() {
return (this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, ""));
}
To use this function, you will use the following syntax:
var s = " Hello World ";
var txt = s.trim();
You can use this extensibility feature in Internet Explorer and Mozilla/FireFox to add new methods and (readonly) properties to the intrinsic objects, but there is no cross browser way to capture the access to properties of those objects.
You might think that it is also possible to add a global method trim(s)
that uses a parameter and the return value to do the same thing, but you might get in conflict with other variables or methods from different libraries using the same name. You can also add a trim
method to the Array
object that handles array items, without getting a conflict.
Some general information about the prototype
property can be found at: MSDN.
JavaScript Prototypes in Mozilla/FireFox
The Mozilla/FireFox platform also offers a proprietary but very useful extension model that allows to also implement properties on the JavaScript intrinsic objects. Here I added a weight
property that returns the sum of the char codes of a string:
String.prototype.__defineGetter__("weight",
function () {
var w = 0;
for (var n = 0; n < this.length; n++)
w += this.charCodeAt(n);
return w;
}
);
You can use this property on any string:
var s = "ABC";
var n = s.weight;
Remember:
The __defineGetter__
and __defineSetter__
are unique features to Mozilla/FireFox and you can find some (still few) samples on the internet by searching through Google for these keywords.
Prototypes with HTML objects
JavaScript objects and HTML objects are completely different kinds of objects when using the Microsoft Internet Explorer but with Mozilla/FireFox, these objects share a common implementation. So, the feature we look at now is only available in Mozilla/FireFox.
The Internet Explorer, for example, supports a innerText
property on many HTML objects like span
, div
, p
, � that can be used to safely access and set the text of an HTML object.
Mozilla/Firefox, in contrary, uses the textContent
property that is part of the newer DOM Level 3 standard to do the same thing.
Now you can use the extensibility features of Mozilla/FireFox to emulate an innerText
property on all HTML objects and you can eliminate all conditional scripting across your JavaScript programs:
var isIE = (window.navigator.userAgent.indexOf("MSIE") > 0);
if (! isIE) {
HTMLElement.prototype.__defineGetter__("innerText",
function () { return(this.textContent); });
HTMLElement.prototype.__defineSetter__("innerText",
function (txt) { this.textContent = txt; });
}
You just have to include this script in one of your common JavaScript include files.
Here are some more usable wrappers to Microsoft specific features:
innerText |
Gets and Sets the inner text of HTML nodes. |
obj.children[] |
Returns the obj.childNodes[] collection. This is not a perfect emulation but probably helps making your code work. |
XMLDocument |
Returns a XMLDocument object from the inner text that should be XML. |
attachEvent |
Attach event handlers to HTML objects. |
detachEvent |
Detach event handlers from HTML objects. |
window.event |
Return the actual executing event object. |
event.srcElement |
Return the target node of the executing event. |
event.cancelBubble |
Stop the propagation of the current event. |
event.returnValue |
Set the return value of the current event. |
xmlobj.selectSingleNode |
Return a single node from an XML element by using an XPath expression. |
xmlobj.text |
Return the inner text of an XML element. |
Conclusion
If you ever thought that writing cross browser compatible JavaScript is hard to do, you now have a chance to build your layer of compatibility and make your script more readable and easier to maintain, and get rid of the conditional code fragments spread all over your web application.
This approach helps a lot but is not the definitive solution. A lot of people (including myself) hope that the current and upcoming standards will get implemented in all browsers. It's better to use these features, when available on multiple platforms. There are also good samples of proprietary Microsoft Internet Explorer features that are so useful that Mozilla/Firefox did implement them. The most popular sample to this is the XMLHTTP
object that allows AJAX like cross browser implementations.
Some good reading URLs for browser compatibility hints:
When implementing AJAX enabled frameworks, JavaScript coding gets more important. There are not many JavaScript libraries that already use this kind of an approach but some newer ones do. Here some links: