miércoles, 13 de julio de 2011

Mostrar y ocultar gadgets con efectos

Sigo con la idea de mostrar y ocultar gadgets agregados a la sidebar de Blogger y ahora, le quiero poner alguna variante que produzca alguna clase de efecto. Ya se sabe que si usamos librerías de alguna clase, estas animaciones son cosas que suelen estar previstas pero, uno quiere eludir eso y en lo posible, hacerlo a mano porque de ese modo, es fácilmente aplicable a cualquier tipo de sitio y en el camino de re-inventar la rueda, algo se va aprendiendo.

Voy a usar el mismo esquema de la entrada anterior para crear ese efecto usando transiciones así que, desde ya, ese efecto no será visible en Internet Explorer pero, el sistema seguirá funcionando perfectamente así que no hay motivo para no intentarlo.

Lo que hacen las transiciones es animar el cambio de una propiedad; es decir, si algo es de color rojo y cambia a color azul, ese cambio no será instantáneo sino que durará cierto tiempo creándose estados intermedios y el color virará desde un rojo a un azul produciendo un efecto de animación. El problema a resolver en este caso es que si permutamos entre display:none y display:block no hay estados intermedios; algo se ve o no se ve y no hay otra posibilidad. Necesitamos usar alguna otra propiedad que podamos variar poco a poco y la única que sirve para eso es height. Esto sería sencillo si funcionaran los porcentajes pero, no es así, hay que saber la altura exacta y expresarla en pixeles.

Cualquier etiqueta de bloque a la que le ponemos la propiedad overflow:hidden y establecemos el valor de height, no se mostrará "completa" si su contenido supera ese valor:

Vivamus adipiscing luctus leo! Integer non elit quam. Donec nisi enim, feugiat id dapibus at, dictum vitae metus? Mauris nisi lorem, porta ut tincidunt ac, semper non lectus. Sed libero ligula; luctus id rutrum sit amet, interdum sit amet odio. Vestibulum varius ligula ac erat tempus commodo.
Vivamus adipiscing luctus leo! Integer non elit quam. Donec nisi enim, feugiat id dapibus at, dictum vitae metus? Mauris nisi lorem, porta ut tincidunt ac, semper non lectus. Sed libero ligula; luctus id rutrum sit amet, interdum sit amet odio. Vestibulum varius ligula ac erat tempus commodo.
Vivamus adipiscing luctus leo! Integer non elit quam. Donec nisi enim, feugiat id dapibus at, dictum vitae metus? Mauris nisi lorem, porta ut tincidunt ac, semper non lectus. Sed libero ligula; luctus id rutrum sit amet, interdum sit amet odio. Vestibulum varius ligula ac erat tempus commodo.

Pero ¿cuál es el valor de height de una etiqueta? Si es una en particular y la medimos, perfecto pero, si luego cambia, habrá que cambiar todo así que hay que buscar alternativas.

Hay un dato que nos da JavaScript: el alto de cualquier elemento visible. Ese dato se lee con clientHeight pero, el elemento debe estar visible y si está visible se arruina el efecto así que lo que se me ocurre es cargarlo fuera de la ventana del navegador, guardar su altura y luego moverlo a su lugar pero dándole una altura de cero con lo cual, quedará oculto.

¿Complicado? Hay más. A esto le sumamos otro problema. Hay gadgets que varían de tamaño cuando interactuamos con ellos. Por ejemplo, el de Archivos que se expande y contrae mostrando más o menos resultados, Si no contemplamos eso, hay partes que quedarán ocultas así que, de alguna manera, hay que detectar esos cambios, permitirlos y volver a re-calcular la altura.

Todo eso, hay que dejárselo al script para que lo que nosotros debamos modificar en la plantilla sea lo mínimo posible.

Voy a hacer lo mismo que hice antes, voy a agregarle una clase a la etiqueta H2 del título y unac clase al DIV inferior de cualquier gadget:
<h2 class='expcon'><data:title/></h2>
<div class='widget-temp'>
..............
</div>
Ahora, voy a poner el CSS básico donde es importante establecer el ancho que es el ancho libre de nuestra sidebar:
<style>
h2.expcon {cursor: pointer;}
.widget-con {
height: 0px;
width: 250px;
overflow: hidden;
-moz-transition: height 1s;
-webkit-transition: height 1s;
-o-transition: height 1s;
transition: height 1s;
}
.widget-temp {
position:absolute;
left: -5000px;
width: 250px;
}
</style>
Uso dos clases, widget-temp es la original, la del gadget sacado de la pantalla; cuando se calcula y se guarda la altura, se "vuelve" a su lugar cambiándole la clase por widget-con.

Aquí hay dos ejemplos simulaciones; el de la izquierda sería un gadget de altura estática y el de la derecha posee enlaces internos que pueden expandirse y colapsarse de manera independiente:

primer ejemplo

Aenean sed est sit amet dolor pellentesque ullamcorper. Mauris ac sapien vel nibh scelerisque tincidunt. In ac nisl quis leo dapibus gravida at id quam. Donec eget nisi nec purus accumsan gravida. Fusce imperdiet sem eu quam sagittis porta. Maecenas luctus porttitor sem in venenatis. Nullam fermentum euismod tristique. Sed massa lorem, pellentesque non malesuada eu; dignissim sed mauris. Etiam venenatis dictum dolor id vestibulum? Cras euismod; lacus a laoreet laoreet, justo magna porta ante, a lobortis nisi neque in lectus. Nam ultricies aliquet mauris at dictum. Proin dapibus velit quis nibh tristique dapibus.

segundo ejemplo

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.

expandir/contraer [+]
expandir/contraer [+]

Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.


Y acá está el script con algunas explicaciones:

<script type='text/javascript'>
//<![CDATA[

// agrega un ID a la etiqueta si se usa Internet Explorer: http://www.codingforums.com/archive/index.php/t-125144.html
var IEelem={};
function IEaddID(fn,uniqueID) {
return function(event) {return fn.call(IEelem[uniqueID],event);}
}

// esta es la función que agregará el evento onclick
function agregarEVENTOtogglewidget() {
// creo una lista con todas las etiquetas H2
var el = document.getElementsByTagName("h2");
// leo la lista una por una
for (var i=0; i<el.length; i++) {
var c = el[i].className; // guardo el contenido del atributo class expcon
// si es una etiqueta H2 con la clase que definí, le agrego un evento que ejecute una función a la que llamo togglewidget
if (c=="expcon") {
if (el[i].addEventListener) {
// Firefox, Chrome, Opera, IE9
el[i].addEventListener("click", togglewidget, false);
} else if (el[i].attachEvent) {
// Internet Explore 8
var uniqueID = el[i].uniqueID;IEelem[uniqueID] = el[i];
el[i].attachEvent("onclick",IEaddID(togglewidget,uniqueID));
}
// busco la etiqueta DIV
var obj = el[i].nextElementSibling || el[i].nextSibling;
obj.altura = obj.clientHeight; // guardo su altura
obj.className = "widget-con"; // le cambio la clase
obj.style.height = "0px"; // la oculto
// y agrego otro evento a ese DIV de tal forma de poder recalcular su altura cuando cambie
if (obj.addEventListener) {
obj.addEventListener("click", recalcular, false);
} else if (el[i].attachEvent) {
var uniqueID = obj.uniqueID;IEelem[uniqueID] = obj;
obj.attachEvent("onclick",IEaddID(recalcular,uniqueID));
}
}
}
}

// esta es la función que permuta el contenido
function togglewidget() {
// hice click en la etiqueta H2 pero, debo ocultar la etiqueta DIV que es la siguiente; así que a busco:
var obj = this.nextElementSibling || this.nextSibling;
if(obj.style.height=="0px") {
// esta oculta así que la muestro
obj.style.height = obj.altura + "px";
} else {
// esta visible así que la oculto
obj.style.height = "0px";
}
}

// esta es la función que recalcula la altura
function recalcular() {
// se pone al 100% para permitir que se expanda verticalmente
this.style.height = "100%";
// se ejecuta una demora para que se efectivice ese cambio
// cambiar 200 por un número mayor si el contenido es "pesado"
var _this = this;
setTimeout(function() {
// y se guarda la nueva altura
_this.altura = _this.clientHeight;
_this.style.height = _this.altura + "px";
}, 200);
}

// una vez que la página web se carga, ejecuto la función
onload=function() { agregarEVENTOtogglewidget(); }

//]]>
</script>

No hay comentarios:

Publicar un comentario