Ejemplo de objeto a modo de pop-up

Los objetos, que pueden definirse como estructuras con propiedades y métodos, pueden relacionarse entre sí de varias formas. Dos de ellas básicas son la relación de herencia y la relación de composición. En este tema aplicaré estos conceptos con un ejemplo práctico y visual para evidenciar su utilidad.

Supongamos que tenemos un objeto funcionando a modo de pop-up o mensaje emergente. En este ejemplo lo usamos para ampliar una definición o el significado de una abreviatura. Al hacer click sobre alguna de las abreviaturas en color nos aparecera un mensaje emergente con la descripción en ese texto de ejemplo:

Ejemplo:

TCP e IP fueron los dos primeros protocolos en definirse y son los más utilizados de la familia. Existen tantos protocolos en este conjunto que llegan a ser más de 100 diferentes, entre ellos se encuentra el popular HTTP, que es el que se utiliza para acceder a las páginas web, además de otros como el ARP para la resolución de direcciones, el FTP para transferencia de archivos, y el SMTP, entre otros.

En este ejemplo tenemos el constructor contenedorPopup() para construir un contenedor con elementos <div>. El contenedor exterior queda referenciado en el objeto con la propiedad this.div y así poder acceder al mismo. En su interior ponemos una especie de botón para cerrarlo y otro <div> para albergar el contenido.

El objeto tiene dos métodos a compartir que incorporamos con el prototipo. Uno es abrir(aqui, enTop, contenido) que nos permite abrir el pop-up tomando como referencia un elemento HTML (aqui) para acceder a su posicionamiento. Con el argumento enTop lo situamos en el punto (0,0) de ese elemento. El argumento contenido es un literal HTML que se ubicará en el pop-up al abrirlo.

Tras crear un par de instancias de ejemplo popup1 and popup2, podemos abrirlos con instancia.abrir(this, false, "...texto..."). Este es el código de todo esto:

En el módulo general.js de este sitio tengo algunas funciones que unifican el distinto comportamiento de navegadores. Ahí se encuentra la función agregarEventListener() para navegadores como IE8 que no soportan addEventListener. También setInnerText() para agregar contenido de texto o nodoPadre para extraer el padre de un elemento. Aunque con las modificaciones del 3 de diciembre de 2012 incorporando los módulos a un espacio de nombres, a estas funciones se accede con Wextensible.general o tambien con un acortador wxG.

Ejemplo de objeto a modo de selector de fuentes

Ahora supongamos que disponemos de otro objeto que nos permite modificar dinámicamente la fuente y su tamaño de un elemento. Aquí tenemos dos párrafos (elementos <p>). Al hacer click en ellos se activan los controles para modificar la familia de la fuente y su tamaño.

Ejemplo:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eu lectus nulla, a volutpat sem. Pellentesque ut lectus non sem hendrerit pellentesque vestibulum nec turpis. Donec sem sapien, aliquam vitae laoreet a, ultrices eget ante. Nam condimentum enim eget sapien semper egestas.

Nulla bibendum turpis sapien, vel vehicula felis. Cras auctor, odio a ultricies vulputate, mauris tellus hendrerit urna, vel consectetur tellus nisl eu nisi. In erat ligula, rhoncus sit amet laoreet eget, volutpat non metus. Integer in eros massa. Mauris iaculis vestibulum tincidunt.

El constructor es ahora selectorFuente(aqui). El argumento aqui es una referencia a un elemento de la página donde vamos a crear los controles, un <select> para la lista de fuentes y un <input> para el tamaño. El objeto tendrá dos propiedades, this.familia y this.tamanyo. Éstas hacen referencia a los elementos HTML que se van a crear. Los dos métodos a compartir los agregamos al prototipo del objeto. Un método es fijarObjetivo() para seleccionar un elemento de texto sobre el que se aplicará la modificación de la fuente. El otro es aplicarFuente() que hará esa modificación. Este método lo ejecutamos como resultado de un evento onchange de los controles de selección y tamaño de la fuente.

En este caso sólo vamos a necesitar una instancia del objeto, pues nos va a servir para modificar cualquier elemento de la página que porte el evento onclick="selFont.fijarObjetivo(this)", incluso este párrafo. La instancia la creamos con var selFont = new selectorFuente(argumento). El argumento es una referencia a un elemento HTML donde incorporar los controles. A continuación mostramos el código:

Aplicando relación de herencia entre objetos

Los dos objetos anteriores podrían haber sido diseñados en diferentes momentos y estar ya funcionando en distintas páginas de forma independiente. Pero supongamos que deseamos que los controles aparezcan en un pop-up anexo al elemento donde vamos a modificar la fuente. Es evidente que si ya tenemos esos dos códigos podríamos relacionarlos de alguna manera para conseguir una tercera funcionalidad combinándolos. Así reutilizamos código y no tenemos que hacer algo distinto para este nuevo propósito. En el siguiente ejemplo, al hacer click en un párrafo aparecerá el pop-up anterior con los controles en su interior para cambiar su fuente con el selector de fuentes.

Ejemplo:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eu lectus nulla, a volutpat sem. Pellentesque ut lectus non sem hendrerit pellentesque vestibulum nec turpis. Donec sem sapien, aliquam vitae laoreet a, ultrices eget ante. Nam condimentum enim eget sapien semper egestas.

Nulla bibendum turpis sapien, vel vehicula felis. Cras auctor, odio a ultricies vulputate, mauris tellus hendrerit urna, vel consectetur tellus nisl eu nisi. In erat ligula, rhoncus sit amet laoreet eget, volutpat non metus. Integer in eros massa. Mauris iaculis vestibulum tincidunt.

La clave está en preguntarnos por la relación que hay entre selectorFuente y contenedorPopup. El selectorFuente es un objeto con un par de controles que son creados en algún sitio. Por otro lado el contenedorPopup nos permite abrir un cuadro emergente con un contenido en su interior. Esa es una relación buscada, que el selector cree los controles dentro del contenedor del pop-up. Podemos hacernos estas preguntas desde la perspectiva de ese requisito que estamos buscando:

  • Relaciones de composición:
    • ¿Puede tener el selector un pop-up?: No, pues el selector es un par de controles que se crean en algún sito para modificar la fuente de algún elemento y no tiene sentido que tenga una caja emergente.
    • ¿Puede tener el pop-up un selector?: No, pues su cometido es mostrar un contenido interior y no modificar fuentes.
  • Relaciones de herencia:
    • ¿Puede ser el pop-up un selector?: No, por lo mismo de antes.
    • ¿Puede ser el selector un pop-up?: Sí, pues además de modificar fuentes puede aparecer en una caja emergente.

Observe la diferencia entre "tener" y "ser" para diferenciar entre relaciones de composición de objetos o herencia entre objetos. Las respuestas afirmativas o negativas sólo cobran significado en el contexto y requisito que estamos buscando, que es ofrecer los controles de modificación de fuentes dentro de una caja emergente. Desde el punto de vista de la programación cualquier respuesta podría ser afirmativa, pero no soluciona el requisito que buscamos. Tal como lo planteamos el selector es, antes que otra cosa, un pop-up. O dicho de otra forma, el pop-up es una super-clase y el selector es una sub-clase que hereda del pop-up su comportamiento. Esto se consigue con el siguiente código:

Le agregamos al prototipo del selectorFuente una nueva instancia del contenedorPopup. Luego creamos una instancia del selector señalando que construya los controles en el elemento que está referenciado en la propiedad this.div del prototipo. Ahora esta instancia selFontPopup nos mostrará ese par de controles dentro de una caja emergente.

Aplicando relación de composición entre objetos

Cuando en el ejemplo anterior respondíamos a la pregunta de si un objeto debe tener otro objeto entonces estamos hablando de composición de objetos. De hecho en los ejemplos anteriores ya se daban relaciones de composición. En el primer ejemplo el constructor contenedorPopup dispone de la propiedad this.div. Así un objeto instanciado con ese constructor como popup1 podrá acceder a ese elemento HTML con popup1.div. Y éste es también un objeto HTML, específicamente un HTMLDivElement. Por lo tanto el objeto popup1 instanciado desde contenedorPopup tiene un objeto div instanciado desde HTMLDivElement.

Forzando un poco el ejemplo último, vamos a suponer que dentro de la caja emergente disponemos de un párrafo de texto. Queremos también que se pueda configurar la fuente de ese párrafo. Este nuevo requisito respondería afirmativamente la pregunta ¿Puede tener el pop-up un selector?.

Ejemplo:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eu lectus nulla, a volutpat sem. Pellentesque ut lectus non sem hendrerit pellentesque vestibulum nec turpis. Donec sem sapien, aliquam vitae laoreet a, ultrices eget ante. Nam condimentum enim eget sapien semper egestas.

Nulla bibendum turpis sapien, vel vehicula felis. Cras auctor, odio a ultricies vulputate, mauris tellus hendrerit urna, vel consectetur tellus nisl eu nisi. In erat ligula, rhoncus sit amet laoreet eget, volutpat non metus. Integer in eros massa. Mauris iaculis vestibulum tincidunt.

Al abrir la caja emergente vemos unos primeros controles para la fuente de los párrafos. Dentro de esa caja hay también otro párrafo cuya fuente se puede configurar con otros dos controles que están por debajo. Este es el código:

Igual que en el ejemplo anterior hacemos que el selector selectorFuente herede de contenedorPopup. Ahora limitamos el ancho a 20em para cuando introduzcamos el párrafo no se extienda horizontalmente en exceso. Creamos una nueva instancia del selector de fuente llamada selFontPopupX. Luego preparamos un elemento de párrafo <p> agregándolo al elemento <div> del contenedor. Recuerde que el prototipo contenedorPopup contiene la propiedad div que es una referencia al contenedor creado. Esta propiedad la hereda el objeto selectorFuente y por tanto su instancia selFontPopupX.

Ahora componemos este objeto (o instancia) selFontPopupX con otro objeto instanciado de selectorFuente. Esto lo hacemos simplemente creando ese objeto como una propiedad del otro con selFontPopupX.selectorPropio. Esto quiere decir que selFontPoupX tiene un objeto de la clase selectorFuente llamada selectorPropio. Por último fijamos el objetivo de este selector propio al párrafo que hemos creado.


Entender y diferenciar los conceptos de herencia y composición nos permiten modelar los objetos y sus relaciones. La herencia tiene la ventaja de relacionar diferentes objetos y darles un comportamiento conjunto como un agregado de ambos comportamientos.