Introducción a AJAX

6.6. Solución cross browser

Las diferencias existentes entre los navegadores disponibles en la actualidad complican en exceso el desarrollo de aplicaciones compatibles con todos los navegadores, llamadas aplicaciones "cross browser" en inglés.

Por ese motivo, se va a diseñar una utilidad que permit unificar la asociación/desasociación de manejadores de eventos, la obtención del objeto event y todas sus propiedades. La utilidad que se muestra se ha obtenido del excelente libro "Professional JavaScript for Web Developers", escrito por Nicholas C. Zakas y publicado por la editorial Wrox.

6.6.1. Asignación de manejadores de eventos

En primer lugar, se crea el objeto que va a englobar todas las propiedades y métodos relacionados con los eventos:

var EventUtil = new Object();

El primer método relacionado con los eventos que es necesario estandarizar es el de la asignación y eliminación de manejadores de eventos:

EventUtil.addEventHandler = function(elemento, tipoEvento, funcion) {
  if(elemento.addEventListener) { // navegadores DOM
    elemento.addEventListener(tipoEvento, funcion, false);
  }
  else if(elemento.attachEvent) { // Internet Explorer
    elemento.attachEvent("on"+tipoEvento, funcion);
  }
  else { // resto de navegadores
    elemento["on"+tipoEvento] = funcion;
  }
};

EventUtil.removeEventHandler = function(elemento, tipoEvento, funcion) {
  if(elemento.removeEventListener) { // navegadores DOM
    elemento.removeEventListener(tipoEvento, funcion, false);
  }
  else if(elemento.detachEvent) { // Internet Explorer
    elemento.detachEvent("on"+tipoEvento, funcion);
  }
  else { // resto de navegadores
    elemento["on"+tipoEvento] = null;
  }
};

6.6.2. Obtención del objeto Event

Para obtener el objeto event, se crea un nuevo método en la utilidad llamado getEvent():

EventUtil.getEvent = function() {
  if(window.event) { // Internet Explorer
    return this.formatEvent(window.event);
  }
  else { // navegadores DOM
    return EventUtil.getEvent.caller.arguments[0];
  }
};

El método getEvent() es un método que no acepta parámetros y que devuelve el objeto event convenientemente adaptado para permitir un comportamiento homogéneo entre diferentes navegadores.

En el caso de Internet Explorer, el objeto event se obtiene directamente a partir del objeto window. Sin embargo, antes de devolver el objeto, se modifica añadiendo las propiedades que no dispone en comparación con el objeto event de los navegadores DOM.

En el caso de los navegadores DOM, el objeto event se obtiene como el primer argumento de la función que actúa como manejador del evento. Como ya se vio en el capítulo de JavaScript básico, la propiedad caller de una función siempre almacena una referencia a la función que la invocó.

Así, si en el interior de un manejador de eventos se hace la llamada al método EventUtil.getEvent(), la propiedad caller será el propio manejador de eventos y su primer argumento será el objeto event. Parece muy abstracto, pero si se piensa detenidamente se comprende fácilmente la solución tan concisa y elegante que se ha utilizado.

6.6.3. Estandarización del objeto Event

El objeto event presenta unas propiedades y métodos muy diferentes en función del tipo de navegador en el que se ejecuta la aplicación JavaScript. Para estandarizar el objeto event, se crea un método que añade al objeto event de Internet Explorer todas las propiedades que le faltan.

El código completo de este método se muestra a continuación:

EventUtil.formatEvent = function(elEvento) {
  // Detectar si el navegador actual es Internet Explorer
  var esIE = navigator.userAgent.toLowerCase().indexOf('msie')!=-1;
  if(esIE) {
    elEvento.charCode = (elEvento.type=="keypress") ? elEvento.keyCode : 0;
    elEvento.eventPhase = 2;
    elEvento.isChar = (elEvento.charCode > 0);
    elEvento.pageX = elEvento.clientX + document.body.scrollLeft;
    elEvento.pageY = elEvento.clientY + document.body.scrollTop;
    elEvento.preventDefault = function() {
      this.returnValue = false;
    };
    if(elEvento.type == "mouseout") {
      elEvento.relatedTarget = elEvento.toElement;
    }
    else if(elEvento.type == "mouseover") {
      elEvento.relatedTarget = elEvento.fromElement
    }
    elEvento.stopPropagation = function() {
      this.cancelBubble = true;
    };
    elEvento.target = elEvento.srcElement;
    elEvento.time = (new Date).getTime();
  }
  return elEvento;
}