Greg's Javascript and DHTML hints and tricks

Below are some tricks and techniques I've found to do some cool things with Javascript in a browser independant way.

XMLHTTP Object

The XMLHTTP object provides a very clean and easy to use remote scripting solution. Even better, this object is now supported on all modern browsers, including everything based on Mozilla, everything based on KHTML (Safari, Konqueror), Opera and IE.

Of course, Microsoft's implementation uses an ActiveX object, so you have to deal with them in a special case. Here's a fairly clean function to load a remote document on any browser, based on an example from Apple's documentation. Note that most browsers won't load a page on a site external to the calling page. I've tested this on IE 6, Firefox 1.0 and Safari 1.2.

function LoadRSDoc(url) {

    if (window.XMLHttpRequest)
        req = new XMLHttpRequest();
	else if (window.ActiveXObject) 
		req = new ActiveXObject("Microsoft.XMLHTTP");
       
	if (req) {
		req.open("GET", url, false);
		req.send(null);
	}
	
	if (req.status == 200)
		return req.responseText;
	else
		return req.status;
}
Custom/Dynamic Attributes

Custom attributes are very handy for storing things you need in a HTML object. For example, you could set an isValid flag on a TD object if it has passed validation. In IE, all you need to do is use the dot operator to access/update these attributes. This seems to work on all browsers except those that are Mozilla based.

A solution that works for all my test browsers was to use the object's setAttribute and getAttribute methods instead, like this:


	var dg = document.getElementById("testTable");
	dg.setAttribute("bar","zzzz");
	alert(dg.getAttribute("bar"));
	
	You can also set custom attributes in the HTML:
	
	<TABLE foo="bar">
	
sprintf

Here's a nice sprintf implementation.

function sprintf()
{
	if (!arguments || arguments.length < 1 || !RegExp)
	{
		return;
	}
	var str = arguments[0];
	var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
	var a = b = [], numSubstitutions = 0, numMatches = 0;
	while (a = re.exec(str))
	{
		var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4];
		var pPrecision = a[5], pType = a[6], rightPart = a[7];
		
		//alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision);

		numMatches++;
		if (pType == '%')
		{
			subst = '%';
		}
		else
		{
			numSubstitutions++;
			if (numSubstitutions >= arguments.length)
			{
				alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).');
			}
			var param = arguments[numSubstitutions];
			var pad = '';
			       if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1);
			  else if (pPad) pad = pPad;
			var justifyRight = true;
			       if (pJustify && pJustify === "-") justifyRight = false;
			var minLength = -1;
			       if (pMinLength) minLength = parseInt(pMinLength);
			var precision = -1;
			       if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1));
			var subst = param;
			       if (pType == 'b') subst = parseInt(param).toString(2);
			  else if (pType == 'c') subst = String.fromCharCode(parseInt(param));
			  else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0;
			  else if (pType == 'u') subst = Math.abs(param);
			  else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param);
			  else if (pType == 'o') subst = parseInt(param).toString(8);
			  else if (pType == 's') subst = param;
			  else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase();
			  else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase();
		}
		str = leftpart + subst + rightPart;
	}
	return str;
}