El objeto respuesta de la clase formEmerge

Un objeto respuesta lo podemos definir como un objeto HTML que almacena valores con la finalidad de comunicarse con el formulario emergente. Se trata de un lugar del documento donde el formulario emergente toma un valor, lo gestiona y luego lo devuelve en ese mismo sitio. Hay dos propiedades de la clase formEmerge que establecen esto:

  • this.respuesta:

    Se trata de una propiedad que almacena un String.

  • this.idObjetoRespuesta:

    Esta propiedad almacena el identificador id de un elemento HTML. Inicialmente y por defecto, cuando creamos una nueva instancia de formEmerge, se crea también un <input type="hidden" /> que se ubica en el body del documento. Estos elementos se identifican con el formulario emergente, pues en su atributo id se incluye el nombre de la instancia. Así cada formulario tiene su <input>, es decir, su propio objeto respuesta. Además podemos volver a referenciarlo con un objeto externo HTML cualquiera, normalmente un control de formulario que disponga del atributo value, aunque también es posible cualquier elemento HTML donde se pueda incluir texto en su interior.

Podemos representar gráficamente el proceso de comunicación como sigue:

proceso objeto respuesta

El elemento HTML ha de disponer de un atributo value, aunque en otro apartado veremos que también es posible con elementos sin este atributo. Ese elemento se referencia en la propiedad idObjetoRespuesta del emergente. Con el método abrir() del emergente ponemos ese valor en su propiedad respuesta. Cuando cerremos aceptando el formulario con el botón aceptar (o bien con aplicar), se genera el método aceptar() (o aplicar() en su caso) produciéndose el proceso inverso, poner el valor almacenado respuesta en el atributo value del elemento HTML, lo que hacemos con document.getElementById(this.idObjetoRespuesta).value = this.respuesta.

Supongamos un ejemplo donde el usuario ha de poner el nombre de la Provincia de su dirección postal. Deseamos que este nombre se unifique según la lista oficial de nombres de provincias, por lo que impediremos que pueda introducir manualmente texto en el cuadro y forzarle a usar el botón adjunto. Este nos llevará a un mensaje emergente que contiene una lista desplegable de las provincias y así el usuario estará obligado a elegir una.

Ejemplo:

En primer lugar declaramos una nueva instancia de la clase formEmerge para tener un formulario emergente que vamos a llamar emergeProvincia. El siguiente código, como el de resto de ejemplos de esta página, lo tenemos en un script a la cabecera de este documento, por lo que es accesible con los menús de los navegadores:

var emergeProvincia = null;
//Carga de la página, donde creamos los formularios emergentes
window.onload = function() {
    ...
    emergeProvincia = new formEmerge("emergeProvincia", "", false, 3);
    var interiorProvincia =  "Seleccione: <select " +
    "onfocus = 'this.value = emergeProvincia.respuesta' " +
    "onchange = 'emergeProvincia.respuesta = this.value' " +
    "onblur = 'emergeProvincia.respuesta = this.value' " +
    ">" +
    "<option value=' '>(ninguna)</option>" +
    "<option value='Álava'>Álava</option>" +
    "<option value='Albacete'>Albacete</option>" +
    "<option value='Almería'>Almería</option>" +
    "<option value='Asturias'>Asturias</option>" +
    "<option value='Ávila'>Ávila</option>" +
    "<option value='etc'>etcétera</option>" +
    "</select>";
    emergeProvincia.nuevoInterior(interiorProvincia);
    emergeProvincia.idObjetoRespuesta = "provincia";
    ...
}

En primer lugar vemos la variable var emergeProvincia = null y otra var interiorProvincia = ... que es un literal HTML para construir el cuerpo interior del mensaje. Vamos a presentar en el emergente una lista desplegable con todas las provincias, aunque en este ejemplo sólo hemos puesto algunas. En el window.onload crearemos la nueva instancia emergeProvincia e incorporaremos el cuerpo interior del mensaje con el método nuevoInterior().

Si recuerda el método abrir

abrir([interior[,titulo[,izquierda[,arriba
      [,ancho[,alto[,sobresale[,foco]]]]]]]])

podíamos pasar un argumento para componer el interior del formulario. Cuando ese interior no varía entre diferentes llamadas al formulario, es conveniente no estarlo creando cada vez que se invoque, pues es una carga innecesaria. Así disponemos del método nuevoInterior(html) para incorporarlos, de tal forma que podemos hacerlo con la carga de la página una sóla vez y al inicio. El argumento html es también una cadena con literal de HTML.

A continuación hacemos emergeProvincia.idObjetoRespuesta = "provincia" donde "provincia" es el identificador id del objeto respuesta que queremos enlazar con nuestro formulario, es decir, el <input> que recibirá el nombre de la provincia y que está junto al botón que abre el formulario:

<label>Provincia:
<input type="text" id="provincia" value=" "
readonly="readonly" />
</label>
<input type="button" value="···"
onclick = "emergeProvincia.abrir('', 'Provincias',this.offsetLeft,
this.offsetTop + this.offsetHeight,'','','',0)" />

El <input> es de solo lectura, por lo que el usuario habrá de presionar el botón al lado que abrirá nuestro formulario emergente. En el botón adjunto que abre el formulario vemos que el primer argumento del método abrir es una cadena vacía, por lo que no incorporaremos el interior a través de este método y así no sobreescribirá el que ya introdujimos con emergeProvincia.nuevoInterior(). Hasta ahora no habíamos usado el argumento foco del método abrir. En este ejemplo hemos pasado el entero 0 que situará el foco en el desplegable del emergente. Esto lo necesitaremos pues más abajo explicamos que se invocará un evento "onfocus" del desplegable, por lo que es indispensable que el foco se sitúe en ese <select> cuando se abra el formulario. Si no se especifica el argumento foco éste se dirige al botón cancelar.

Veáse que en este formulario hemos pasado el argumento conBotones igual a 3 en el constructor de emergeProvincia. Así tendremos los tres botones aceptar, cancelar y aplicar. El botón aplicar se comporta como aceptar a excepción de que no cierra el formulario. Cuando en un formulario emergente se pulsa aceptar o aplicar entonces pone su propiedad emergeProvincia.respuesta en emergeProvincia.objetoRespuesta.value, que al referenciarlo al externo realmente lo está poniendo en el <input id="provincia" > anterior. Con los botones cancelar y cerrar no ejecuta esta acción.

El interior del formulario emergente contiene un desplegable <select>, cuyo comportamiento hemos de declarar explícitamente, pues dependerá del tipo de contenido que deseemos ubicar en ese interior. Así ponemos:

"onfocus = 'this.value = emergeProvincia.respuesta' " +
"onchange = 'emergeProvincia.respuesta = this.value' " +
"onblur = 'emergeProvincia.respuesta = this.value' " +

Primero vemos que con "onfocus" cogemos el valor de la respuesta del formulario y lo ponemos en el desplegable. Esta respuesta ya había sido incorporada automáticamente desde el elemento <input> externo mediante el método abrir(). Pero para que se ejecute este evento del desplegable necesitamos situar el foco, lo cual hicimos con el argumento foco del método abrir que vimos antes. Este argumento puede pasarse como un número entero que será el de orden del control del formulario, pues hemos de recordar que nuestros formularios emergentes son elementos <form> y que podemos acceder a los controles de un formulario con formulario[n] siendo n el orden de aparición de los mismos. También podemos pasar como argumento de foco una referencia directa o bien un string del identificador id de un elemento HTML cualquiera del formulario, por supuesto, elemento que debe ser capaz de recibir el foco (como los controles de formulario por ejemplo o los vínculos <a>).

Luego cuando modifiquemos el desplegable seleccionando otra provincia ("onchange") o bien cuando salgamos del desplegable ("onblur"), ponemos el valor en emergeProvincia.respuesta, que luego será llevado de vuelta al objeto respuesta cuando pulsemos el botón aceptar o aplicar mediante los métodos aceptar() o aplicar().

La finalidad de separar la respuesta del valor del objeto respuesta es porque podemos hacer ejecuciones dentro del interior del emergente e ir modificando esa propiedad emergeProvincia.respuesta, pero sin tocar el value del objeto identificado con this.idObjetoRespuesta, valor que única y exclusivamente será modificado de forma automática cuando aceptemos el formulario. La respuesta es un string pues su uso va, principalmente, destinado a cubrir el atributo value de elementos <input> como objetos respuesta.

Internet Explorer 8 y atributos vacíos

En la clase formEmerge hacemos uso de elemento.getAttribute("value") para obtener el valor de ese atributo value de un elemento HTML. Si el elemento no posee ese atributo debería dar null. Sin embargo cuando el valor es una cadena vacía "", Internet Explorer no considera que el elemento posee el atributo. Incluso usando elemento.hasAttribute("value") da falso cuando value="". El resto de navegadores Opera 10.6, Firefox 3.6 y Safari 4.0 si reconocen las cadenas vacías y no ignoran el atributo.

Aunque lo hemos arreglado en formEmerge para que responda bien a esta particularidad de IE, hemos de tenerlo en cuenta para el comportamiento de otros elementos como el select, pues el atributo value del primer option si lo pusiéramos a vacío, IE no lo incluiría como atributo. Así en el primer ejemplo ponemos un espacio " " para el value del <input>, mientras que para el ejemplo del siguiente apartado ponemos un &nbsp; que es un espacio no separable para incluir dentro del elemento <strong>.

Un espacio no separable es como un espacio normal, pero con la cualidad de que el navegador no lo elimina, como hace cuando en el HTML hay más de un espacio convirtiéndolo en uno sólo, o bien cuando se extrae el texto interior de un elemento con innerText de Internet Explorer, función que yo he adecuado a otros navegadores con getInnerText() y que se usa dentro de la clase formEmerge.

Usando otros elementos HTML como objetos respuesta

El objeto respuesta puede ser cualquier elemento HTML que disponga de un atributo value como los controles de formularios. Pero también podemos usar elementos HTML no vacíos y que permiten texto en su interior. Para verlo hacemos otro ejemplo exactamente igual que el anterior pero usando un elemento HTML <strong> como objeto respuesta:

Ejemplo:

Provincia:  
El código de este elemento y su botón adjunto es el siguiente:
Provincia: <strong id="provincia2"
style="border: solid 1px; padding-left: 0.5em; 
padding-right: 0.5em;">&nbsp;</strong>
<input type="button" value="···"
onclick = "emergeProvincia2.abrir('', 'Provincias',this.offsetLeft,
this.offsetTop + this.offsetHeight,'','','',0)"
/>
    

Hemos construido el formulario emergeProvincia2 exactamente igual que el del ejemplo anterior:

...
var emergeProvincia2 = null;
//Carga de la página, donde creamos los formularios emergentes
window.onload = function() {
    ...
    emergeProvincia2 = new formEmerge("emergeProvincia2", "", false, 3);
    var interiorProvincia2 =  "Seleccione: <select "... esto es igual ...
    emergeProvincia.nuevoInterior(interiorProvincia2);
    emergeProvincia.idObjetoRespuesta = "provincia2";
    ...
}

La forma en que el emergente toma o deja el valor en el objeto respuesta para este caso se consigue modificando el nodo texto del interior del elemento ya que no dispone del atributo value como los controles de formulario. Para ello he diseñado las funciones getInnerText() y setInnerText() pues todos los navegadores no se comportan igual a la hora de tratar el nodo texto de un elemento.