Window onError Event

Window.onerror is an obscure and little used event with surprising usefulness. Before the days of FireBug it was difficult to debug in the browser, where an error occurred and why. Now that we have FireBug, most JavaScript works wonderfully in FireFox and decently in other browsers. And even with the robust debugging, there will still be user configurations that were not tested against, who see JavaScript errors that you can never capture.

The onerror event allows assigning a listener function to be called every time an error occurs in your JavaScript code. That arguments passed to the callback function are: the browser message, URL of the current page, and the line number that triggered the error. With this information you have enough information to debug almost any error. Additionally, assigning a function to onerror, also prevents errors from bubbling to the browser, and showing those annoying browser popups on JavaScript errors.

Surprisingly this event has been around since IE 4, Netscape 3, and FireFox 1, so it is well supported by current browsers. Here is a simple example of how to subscribe to the onerror:

Example 1: Simple onerror function

// declare this early in your codebase
window.onerror = function(message, url, lineNumber) {
	// code to execute on an error
	return true; // prevents browser error messages
};

Something I am experimenting with is to include this code in a file (error.js) right after I initialize my Framework. error.js contains my onerror listener, catching all errors and exception, which is used to send AJAX messages to the back-end, logging all JavaScript errors server-side. Moreover, if you use a client detection method, then you can also log the browser and OS that caused the error for repeating later. Using YUI connection manager you would do something like:

Example 2: Error Logging

window.onerror = function(message, url, lineNumber) {
	var params = [];
	params[0] = 'browser=' + Core.Client.browser;
	params[1] = 'data=' + navigator.userAgent + '|' + navigator.vendor + '|' + navigator.platform;
	params[2] = 'lineNumber=' + Core.Client.message;
	params[3] = 'message=' + Core.Client.lineNumber;
	params[4] = 'os=' + Core.Client.OS;
	params[5] = 'url=' + Core.Client.url;
	params[6] = 'version=' + Core.Client.version;
	
	try {
		YAHOO.util.Connect.asyncRequest('GET', 'logJavaScriptError.event?' + params.join('&'), function() {}, null);
	}
	catch(e) {
		// squelch, because we don’t want to prevent method from returning true
	}
	
	return true;
};

In this case, we log the line number, url, and browser error message, in additional to client information. I also go ahead and send all possible browser variables, for future parsing in case I could not properly discern the browser programatically. The AJAX request, is simply a fire-and-forget pattern, because we don’t really care if it is successful or not. We assume that 99% of the time it will succeed, so that these errors are properly logged. If there is an exception thrown in the AJAX code, say by YUI because the URL is not found, then this method would not completely execute. However, if you wrap the AJAX in a try/catch, you can ensure that the end of the method is reached and true is returned, to prevent errors from being shown to the browser.

I have created a test page, which tries to use a non-existent variable, thereby throwing an error. One thing you’ll probably notice is that you don’t get that pesky JavaScript error message in IE, however, you also do not get any FireBug goodness in FireFox. For FireBug to detect errors, the error must be allowed to bubble to the browser, so you will probably want a constant to turn off/on a debug mode, so that you can still use FireBug for development.

I also played around with using Framework Event handlers, however, none of the Frameworks I tested (YUI, MooTools, and Prototype) special-case an event handler for window.onerror. There are two problems with window.onerror that requires special-case event handling. The first problem is that when using event handlers, the callback function will only be passed the event object, instead of the three parameters that should be passed. Second, returning true does not stop the error bubbling, nor does preventing propagation and/or bubbling, so you cannot prevent errors from bubbling to the browser. You will have to assign your error handling method, directly to window.onerror, and replace any implementation that your framework might use.

Fortunately (at the time of this writing), after doing a quick search through those sample frameworks, I found that only YUI attempts to implement the framework code, and only in their logger.js file. In the definition of the Logger object, you can specify whether to use an onerror callback or not. I believe then, it is relatively safe, to play around with this event.