Cabeza de Ratón: versus el Amor Letal

VtnDrag08

Introducción

Este artículo explica una manera de implementar un conjunto de ventanas con MooTools. Continúa con la serie de ejemplos propuestos desde hace un tiempo.

El resultado de este artículo se puede apreciar en este Link.


Ventanas con MooTools.

Concepto de Funcionamiento

Antes de meternos a pleno con el código se tiene que captar la idea general de como implementar y como funciona este script.
Entonces … nuestras ventanas están definidas dentro del código html de la página el cual recorreremos mediante MooTools buscando todos los componentes de las mismas; tales como cabecera, botón de cerrar, botón de dimensión y zona de contenido.
Es muy importante mantener la estructura de las ventanas (layouts) ya que de lo contrario el comportamiento del script no será el esperado.

layouts – Código HTML

Presentamos en primera instancia el código html de una sola ventana seguido de un simple esquema para tener una idea gráfica de lo que estamos hablando.

<div class="vtn">
    <div class="vtnCabecera">Titulo</div>
    <div class="cerrar"></div>
    <div class="vtnContenido">
        <p>Aca va el contenido</p>
        <p>Podemos poner lo que se nos antoje !!!.</p>
    </div>
    <div class="tamanio"></div>
</div>


Layout de una Ventana

El contenedor de una ventana se identifica mediante la clase de css class=”vtn”. Estoy es MUY importante, porqué será mediante este identificador de clase que nuestro código javascript, de la mano de MooTools, generará TODAS las ventanas de la página web.

En otras palabras, si queremos que un elemento html de nuestra página sea una ventana más tiene que tener definido, como mínimo, una clase css con valor igual a vtn. Si observan el código fuente del ejemplo verán que esta estructura de ventana se repite cinco veces, variando solo aquello que no la afecte, como el título o el contendo.

Por otro lado se puede apreciar 4 zonas o elementos por ventana. O sea … tenemos 4 nodos hijos que conforman la estructura.
Lo importante en este caso es que mantengamos 4 y solo 4 nodos. Acá no es necesario definir clases css, identificador id o Tag HTML en cuanto al funcionamiento se refiere. Sin embargo definimos, por ejemplo, class=”cerrar”; para diseñar el aspecto gráfico/funcional y No su comportamiento propiamente dicho.
También es importante el orden de aparición de los nodos componentes. Así tenemos que el primer nodo hijo, que en este caso le definimos class=”vtnCabecera” no debe cambiar de posición.

En este ejemplo hemos definido divs como nodos de ventana; pero nada impide que utilicemos otros TAGs, por ejemplo, listas ul – li.
Respetando estas simples reglas de diseño evitaremos grandes errores.

El Script

Identificar Todas las Ventanas
Para identificar todas las ventanas de nuestra página nos valemos de la casi mágica función de MooTools $$(). Acá es donde tenemos que definir el criterio de filtro de los nodos DOM a recolectar. Se acuerdan de class=”vtn” ?. Bueno … en esta línea entra en juego esta definición:

var vtns = $$('.vtn');

De esta manera definimos la vaiable vtns como un array donde cada elemento es un nodo que terminará siendo una ventana del sistema.

Definición de variables de comportamiento
Necesitamos definir estas variables para el comportamiento de elevación de capa cuando ponemos en foco cada vetana.

var zIndexBase = 10;
var zTempo = zIndexBase;
zIndexMayor = vtns.length + zIndexBase - 1;

Con IndexBase seteamos el z-index de la ventana de más abajo. Esto nos sirve para adaptar la altitud del conjunto de ventanas a nuestro diseño. En el ejemplo el menor z-index está definico an 10. zTempo es una variable temporal que utilizamos para definir los niveles z-index de todas las ventanas, inicialmente tiene el mismo valor que IndexBase. Finalmente zIndexMayor tiene el valor z-index de la ventana de más arriba.

Construcción Ventana por Ventana

Ahora recorremos todas las ventanas mediante vtns.each(el) donde el es el identificador de elemento de cada ventana. Le definimos posición y z-index (acá entra en escena la variable temporal zTempo) mediante las funciones de estilo setStyle y setStyles.

el.setStyles ({
    top: ((zTempo - zIndexBase + 2) * 30 + 'px'),
    left: ((zTempo - zIndexBase + 2) * 50 + 'px')
    });

el.setStyle ('z-index', zTempo);
zTempo++;

Tómese parte de este código como algo necesario para la ubicación inicial de las ventanas … con un logaritmo poco elegante. El eje central de este artículo no se centra en este punto. Lo importante es como se definen los z-index en el.setStyle (‘z-index’, zTempo).

Ventana Individual

Ahora construimos y configuramos los elementos de cada ventana. En forma similar a lo realizado con el array de ventanas, generamos la variable vtn (singular) que contiene los componentes de una ventana:

var vtn = el.getChildren();

Fijense que simplemente asignamos al array vtn todos los elementos hijos de cada ventana mediante getChildren();. Es por esto que hacemos hincapié en el cuidado que hay que tener al momento de diseñar los layouts. Cada elemento (cabecera, botón de cerrar, etc … ) tiene una ubicación dentro de la estructura generando una correspondencia directa entre los elementos de este array y el nodo de nuestra estructura. Así, por ejemplo, el primer elemento del array vtn[0] es el div class=”vtnCabecera”.

Construyendo …
Las hacemos Desplazables y Redimensionables. Hay dos artículos que explican en forma detallada este tema. vtnDrag01 y vtnDrag05

//  Hacemos las ventanas Arrastrables
//  El elemento de Dimension es el primer nodo hijo vtn[0]
    el.makeDraggable({
    handle: vtn[0],
    container: el.getParent()
    });

//  Ahora Dimensionables.
//  El elemento de Dimension es el tercer nodo hijo vtn[3]
    el.makeResizable({handle: vtn[3]});

Lo importante, una vez más, en esta parte es notar como identificamos cada nodo. Usamos vtn[0] y vtn[3] para la cebecera y para el punto de redimensión respectivamente. vtn es un array, así que vale !

Foco de las Ventanas
Ahora diseñamos el comportamiento de las ventanas al hacer click sobre cada una de ellas. En realidad usamos mousedown. Agreguemos el evento y definamos el comportamiento:

//  Agregamos evento MouseDown a la ventana Clickeada
    el.addEvent('mousedown', function(){
    //  Rutina de Niveles de Ventanas Z-INDEX
    var zIndexActual = el.getStyle ('z-index');

    for (i = 0; i <= zIndexMayor - zIndexBase; i++) {
        if (vtns[i].getStyle('z-index')> zIndexActual) {
            var zIdx = vtns[i].getStyle ('z-index').toInt() - 1;
            vtns[i].setStyle ('z-index', zIdx);
            }
        };

    //  Levantamos la ventana
    el.setStyle ('z-index', zIndexMayor);
});

Acá nos podemos marear un poco. Esta rutina se encarga de, una vez que se produjo mouseover, reconfigurar el parámetro z-index para que:
1 – Las ventanas que están por encima de la ventana seleccionada disminuyan un valor su z-index en uno.

2 – La ventana cliqueada pase a tener el valor mas alto mediante la variable zIndexMayor inicialmente definida.

Nada del otro mundo.

También definimos el mismo comportamiento para la cabecera de cada ventana:

vtn[0].addEvent ('mousedown', function () {
    el.fireEvent ('mousedown');
    });

El código lo interpretamos así: Cuando hacemos mousedown en la cabecera de la ventana (vtn[0]) disparamos el evento mouseover de la ventana antes definido. Lo logramos gracias a fireEvent(). No es buenísimo ?. Tan fácil y tan potente.

Finalmente, definimos el comportamiento al cerrar la ventana:

vtn[1].addEvent ('click', function () {
    el.setStyle ('display', 'none');
    });

A esta altura del partido, si no entendes esto, tampoco has entendido nada de lo anterior. Esta funcionalidad es simplemente para adornar un poco más el comportamiento final. Ya haremos otra versión y seguiremos robando con el mismo artículo. ;-)

Agregamos Estilo

Es importante definir un buen estilo para que funcione elegantemente nuestro script. Presten atensión a las hubicaciones de cada elemento.

body {
    font-family: Arial, Verdana, sans-serif;
    font-size: 12px;
    font-weight: normal;
    color: #333333;
    margin: 0px;
    }

/* Ventanas */
.vtn {
    width: 250px;
    height: 200px;
    border: 2px solid #136;
    background: url('fnd01.png');
    }

.vtnCabecera {
    background: url('fnd02.png');
    color: #EEF;
    font-weight: bold;
    font-size: 12pt;
    text-align: center;
    padding: 0 0 2px 0;
    height: 17px;
    cursor: move;
    }

.vtnContenido {
    padding: 3px;
    }

.tamanio, .cerrar {
    position: absolute;
    bottom: 0px;
    right: 0px;
    height: 17px;
    width: 17px;
    cursor: se-resize;
    background: url('tamanio_16x16.gif') no-repeat;
}

.cerrar {
    cursor: pointer;
    top: 0px;
    background: url('cerrar.png');
}

#contenido {
    width: 600px;
    height: 550px;
    background: #CCC;
}
Todo el Código JavaScript

Finalmente presentamos todo el código JavaScript.

Window.onDomReady(function() {

    //  generamos Array de Ventanas
    var vtns = $$('.vtn');

    //  zIndexBase - refleja el z-index de nuestras ventanas
    var zIndexBase = 10;
    var zTempo = zIndexBase;

//  zIndexMayor es el z-index de la ventana de mas arriba
    zIndexMayor = vtns.length + zIndexBase - 1;

    //  Generamos todas las ventanas
    vtns.each(
        function(el){

            //  Agregamos desplazamiento a todas las ventanas, una por una.
            el.setStyles ({
                top: ((zTempo - zIndexBase + 2) * 30 + 'px'),
                left: ((zTempo - zIndexBase + 2) * 50 + 'px')
            });

            el.setStyle ('z-index', zTempo);

            zTempo++;

        //  generamos Array con Elementos de cada Ventana (hijos)
            var vtn = el.getChildren();

        //  Hacemos las ventanas Arrastrables
            el.makeDraggable({
                handle: vtn[0],
                container: el.getParent()
                });

            //  Ahora Dimensionables. El elemento de Dimension es el tercer nodo hijo vtn[3]
            el.makeResizable({handle: vtn[3]});

            //  Agregamos evento MouseDown a la ventana Clickeada
            el.addEvent('mousedown', function(){
                //  Rutina de Niveles de Ventanas Z-INDEX
                var zIndexActual = el.getStyle ('z-index');

                for (i = 0; i <= zIndexMayor - zIndexBase; i++) {
                    if (vtns[i].getStyle('z-index')> zIndexActual) {
                        var zIdx = vtns[i].getStyle ('z-index').toInt() - 1;
                        vtns[i].setStyle ('z-index', zIdx);
                        }
                    };

                //  Levantamos la ventana
                el.setStyle ('z-index', zIndexMayor);

            });

            //  Tambien agregamos MouseDown a la cabecera de la ventana
            vtn[0].addEvent ('mousedown', function () {
                el.fireEvent ('mousedown');
                });

            //  Agregamos evento MouseDown boton de cerrar ventana
            vtn[1].addEvent ('click', function () {
                el.setStyle ('display', 'none');
                });
        });
    });

Utilizamos Window.onDomReady para asegurarnos de ejecutar TODO este código una vez cargado el contenido html de la página. onDomReady de MooTools es realmente más eficiente que windows.onload ya que no contempla la carga de imagenes para que se dispare el evento.

Finalizando

Una vez más espero que este artículo nos sirva para comenzar a comprender algunas metodologías de diseño. Sin queres ser demagógico; lo mío es mucha práctica y poca teoría. Seguramente el funcionamiento se puede optimizar un poco más. Nos queda pendiente la construcción de un plugIn que nos permita implementar las ventanas de una manera práctica y sencilla.

  • Quizás le falte algún tipo de overflow:hidden a la capa que contiene el texto. Si empequeñecemos mucho el ancho de la ventana el texto se hace más alto (evidentemente) y en un momento dado llega a salirse de la “ventana virtual” por abajo.

    Por otra parte un overflow:hidden impediría ver el contenido cuando se cambia el tamaño de las ventanas creo, así que no tengo muy claro cómo se puede solucionar. Puede que midiendo la altura del texto y de la capa…

    Un saludo.

  • Gracias Darke. La verdad es que el tutorial lo hice pensando en seguir mejorandolo … estaría bueno hacer un plugIn. Lo encaramos ?
    Saludos.

  • Una pregunta… estas ventanas prevalecen por encima de un flash movie o un combobox?
    Tenia un jsdialogs pero nunca logre hacer qu queden por encima de esos objetos…
    Lindo articulo.

  • No lo se … en forma natural creo que siempre la pelicula flash queda por sobre cualquier cada, mas allá del valor del z-index. De todas formas no tengo experiencia con flash. Gracias por tu comentario Holden.

You can follow any responses to this entry through the RSS 2.0 feed.