Saturday, March 25, 2006

A few useful JavaScript utility functions

Here's some useful JavaScript utility functions. Firstly, here's the modern, recommended way to add an event listener to an object:
function add_event(elem,event_name,func)
{
 if(elem.addEventListener)
    {
    elem.addEventListener(event_name,func,true);
    return true;
    }
 else if(elem.attachEvent)
    return elem.attachEvent("on"+event_name,func);
 else
    return false;
}
elem is the object that you want to be listening for the event. You would typically obtain this with document.getElementById(). event_name is the name of the event you are listening for, e.g. click, mouseover, load etc. func is the name of the function you wish to be called when this event occurs. An example: say you have a button, ID theButton, and you want the function handle_button to be called when it is clicked. Your HTML: <input type="button" name="theButton" is="theButton" value="CLICK" />. And the event listener is added in your JavaScript with add_event(document.getElementById('theButton'),'click',handle_button);. Note that the func is just the name of the function, without arguments. This method of adding event listeners is recommended, since you're not cluttering your code with lots of onclick="…";" etc. A good idea is to add an onload listener to the window, and then set up all the subsidiary listeners within the handler function:
function window_init(evt)
{
  add_event(document.getElementById('element1'),'click',handle_click);
  …
}

function handle_click(evt)
{
  if(!evt && window.event)
     evt=window.event;

  event_target=get_target(evt) // see later
  alert(event_target.getAttribute('id');
}

add_event(window,'click',window_init);

An event can potentially be handled by more than one document element. For example, a form's submit button can trigger both the button's click handler and the form's submit handler. Sometimes that's not what you want. You need a method to stop the event proceeding further:
function kill_event(e)
{
  if(window.event)
     {
     window.event.cancelBubble=true;
     window.event.returnValue=false;
     }
  else if(e && e.preventDefault && e.stopPropagation)
     {
     e.preventDefault();
     e.stopPropagation();
     }
}
You might want to use this function if a form fails validation, to stop it being submitted. Under Firefox/Mozilla, all event handler functions receive the event object as an argument. For Internet Explorer, the global window.event object is set. If you pass either of these objects to kill_event(), it will proceed no further. It's also a good idea to return false from the event's handler function.
You frequently want to know the element that received a mouse event. Unfortunately, the two main flavours of browser store this info in different places. This function user browser object detect to select the appropriate one:
function get_target(evt)
{
  if(evt.srcElement)
     targ=evt.srcElement;
  else if(evt.target)
     targ=evt.target;
  else
     targ=false;

  return targ;
}

Many DHTML effects need to know the absolute position of an element in screen coordinates. This pair of functions will work in just about any browser:
function get_x_pos(elem)
{
  var curleft=0;
  if(elem.offsetParent)
     {
     while(elem.offsetParent)
        {
        curleft+=elem.offsetLeft
        elem=elem.offsetParent;
        }
     }
  else if(elem.x)
     curleft+=elem.x;

  return curleft;
}

function get_y_pos(elem)
{
  var curtop=0;
  if(elem.offsetParent)
     {
     while(elem.offsetParent)
        {
        curtop+=elem.offsetTop
        elem=elem.offsetParent;
        }
     }
  else if(elem.y)
     curtop+=elem.y;

  return curtop;
}
The elem parameter is the object whose x- or y-position you want, as returned by (e.g.) document.getElementById().
Another important piece of information is how far the window has been scrolled. You need to know this, for example, if you wish to ensure an element remains positioned in the viewport. The following pair of functions reutnr the horizontal and vertical scroll for the current window object:
function get_x_offset()
{
  var x;
  if(window.pageXOffset)
     x=window.pageXOffset;
  else if(document.documentElement && document.documentElement.scrollLeft)
     x=document.documentElement.scrollLeft;
  else if(document.body)
     x=document.body.scrollLeft;
  return x;
}

function get_y_offset()
{
  var y;
  if(window.pageYOffset)
     y=window.pageYOffset;
  else if(document.documentElement && document.documentElement.scrollTop)
     y=document.documentElement.scrollTop;
  else if(document.body)
     y=document.body.scrollTop;
  return y;
}

Getting the mouse position is in general impossible, because the different minority browsers implement a different set of objects and can also masquerade as other browsers. The following will work in the majority of cases:
var gx,gy;

function get_mouse_pos(evt)
{
  if(event.clientX)
     {
     gx=event.clientX+document.body.scrollLeft;
     gy=event.clientY+document.body.scrollTop;
     }
  else
     {
     gx=evt.pageX;
     gy=evt.pageY;
     }

  gx=max(gx,0);
  gy=max(gy,0);

  return true;
}

function max(a,b)
{
  if(a>b)
     return a;
  return b;
}
The max function helps to correct for an anomaly in some versions of Mozilla where coordinates can become negative.