Javascript con Furoya: estilos al padre según relación de aspecto de la imagen hija

Hay situaciones que necesitan distintos estilos en función del formato de una imagen contenida en un elemento. Si es vertical quizás queramos el texto a su lado y si fuese horizontal debajo. Aquí Furoya nos muestra cómo lograrlo.

Javascript con Furoya: estilos al padre según relación de aspecto de la imagen hija

✎ 3
COLABORACIÓN AUTOR INVITADO

Poco antes de la publicación de este artículo hubo un (otro) cambio importante en el formato del blog. Y uno de los efectos agregados con javascript es el de cambiar el estilo de un elemento según la orientación de su imagen nieta. Si ésta es apaisada, va un flex, y si no, va otro.

Ya que está publicado y no voy a arruinar la primicia, aprovecho para mostrar (más que explicar) cómo trabaja ese escript. Además de agregarles otras versiones.

Ya hemos visto cómo se encuentra e identifica un elemento en el DOM para leerlo o modificarlo. Y de entre los valores para leer está el de tamaño. En realidad, hay varias formas de obtener las medidas de una imagen para después saber si es más ancha o más alta. Una de las más efectivas es:

laImagen.naturalWidth; //PARA EL ANCHO laImagen.naturalHeight; //PARA EL ALTO

que captura las medidas originales ignorando las aplicadas por el navegador (ojo!, no las aplicadas por el servidor).

El problema con este método es la poca compatibilidad con navegadores viejos. Allí, teniendo en cuenta que aún con formatos aplicados la imagen seguro va a mantener la relación de aspecto, conviene usar el ya mencionado:

laImagen.offsetWidth; //PARA EL ANCHO laImagen.offsetHeight; //PARA EL ALTO

Por supuesto que dependiendo del caso también podemos echar mano de:

laImagen.style.width; //ANCHO CSS laImagen.style.height; //ALTO CSS laImagen.width; //ANCHO HTML laImagen.height; //ALTO HTML

una vez que obtenemos los valores (para CSS hay que quitarles la unidad) sólo resta comparar si es más ancha que alta, o no; y aplicar el formato dependiendo del caso.

Solventando inconvenientes

Pero aquí ya nos empezamos a encontrar con algunos problemas. Resulta que las medidas hay que tomarlas cuando se carga la imagen, por lo que el escript debería ejecutarse después.

Para eso tenemos un evento al que se llama con el atributo onload="" dentro de la imagen. Pero si la imag la manda escrita el servidor, no siempre podemos modificar ese código fuente. Entonces vamos a agregar el atributo al vuelo usando addEventListener, setAttribute( y/o attachEvent.

La forma de escribirlos (y los casos, por retrocompatibilidad) ya están explicados en muchos tutoriales, no los voy a aburrir con eso aquí también.

Pero sí voy a mencionar que para el caso de onload en las imágenes, tenemos una pequeña dificultad : la inclusión del evento debe ocurrir después de la carga del HTML correspondiente, pero antes de la llamada al archivo de imagen.

Eso nos complica un poco. La mejor manera (que se usó en Kseso CSS)* fue poner el escript inmediatamente después de la imagen que decide la modificación de su abuelo. Es muy difícil que una máquina sea lenta para procesar un HTML y rápida para descargar un archivo, así que suponemos va a funcionar.

* Nota del Editor: Como la parte afectada por el script sólo se carga en el índex, con un condicional de Blogger, así se evita también su presencia y efectos en otras páginas del blog.

La siguiente es una copia más limpia del original, donde se puede apreciar el uso de código casi sin function's, ejecutando javascript a medida que el navegador lo lee.

<!DOCTYPE html> <html lang="es-ar"> <head> <meta charset="utf-8" /> <style type="text/css"> .item-0 { padding: 5px; text-align: center; } .apaisada { border: solid 5px red; } .columna { border: solid 5px blue; } </style> </head> <body> <h1>Cambio de clase dependiendo de la orientación de la imagen.</h1> <article class='item-0'> <div class='img-index'> <img alt='Imagen prueba.' src='https://lh3.googleusercontent.com/-oJwM7CqBEBk/VXc0iVEe2LI/AAAAAAAADBs/NFgiRwnqwnY/s144-Ic42/AlejandroPuente.jpg'/> </div> <script type='text/javascript'> //<![CDATA[ var observa = document.querySelector(".item-0"); var contempla = observa.querySelector(".img-index img"); contempla.addEventListener("load", function(){ if(contempla.naturalWidth > contempla.naturalHeight) observa.classList.add("apaisada"); else observa.classList.add("columna"); }) //]]> </script> </article> <p>Si la imagen es apaisada, el borde de su "abuelo" es rojo, de otra modo (cuadrada o acartelada), es azul. </p> <h2>Imágenes de prueba (para copiar y pegar en el código HTML).</h2> <p> <img src="https://lh3.googleusercontent.com/-oJwM7CqBEBk/VXc0iVEe2LI/AAAAAAAADBs/NFgiRwnqwnY/s144-Ic42/AlejandroPuente.jpg" alt="Alejandro Puente." title="Alejandro Puente." /> <img src="https://lh3.googleusercontent.com/-CPWfsY8gNzk/U0miaEmftGI/AAAAAAAAAQE/uS3OQgFoWFY/s144-Ic42/LorenaFernandez.jpg" alt="Lorena Fernández." title="Lorena Fernández."><p> </body> </html>

Y ésta es la versión para probar:

See the Pen Clase según alto y ancho de imagen.(1) by solipsistaCP (@solipsistacp) on CodePen.

Es un código "moderno", usa querySelector; addEventListener; naturalWidth y classList.add. Como no viene al caso ni es la finalidad del artículo no agrego estilos complejos en estas pruebas; le puse un borde al contenedor y un color para cada caso. Con esto alcanza para verlo.

2ª versión: cambio "al vuelo"

Una incomodidad es que al probarlo hay que reemplazar a mano la ruta de la imagen. Claro, en una página real se carga sola y no se cambia; pero como estos son códigos de laboratorio vamos a ver ahora otra versión que tiene de regalo el ejemplo de cómo se reemplazan imágenes en una galería con "click". Y algunos comentarios que no puse en el anterior para seguir el funcionamiento.

See the Pen Clase según alto y ancho de imagen.(2) by Kseso (@Kseso) on CodePen.

Ahora ya tenemos un código más complejo, más compatible y más completo para ser adaptado.

De entrada, vemos que el JS está puesto en el head, y nos ahorramos el insertar cualquier onload en la imagen, ya que disparamos una función al cargarse el documento, y seguramente la imagen ya está.

Así la ubicamos para después medirla con una segunda función. Esto es útil porque tenemos botones para alternar las imágenes y probar cómo se modifica el borde de su contenedor abuelo, y una vez hecho el cambio, solamente se dispara esa segunda función. Un inconveniente puede aparecer en páginas muy grandes, porque el cambio no se va a ver hasta que cargue todo el HTML.

Como se puede apreciar en el source, las diferencias para encontrar el elemento usando "código viejo" son muchas. Hay que ir desde document bajando elemento por elemento y como usamos sus clases, hay que manejarse con colecciones, lo que obliga a poner el número de índice al final de cada línea. Para nuestro caso no es grave, siempre va a ser [0] porque apuntamos al primer (y único) elemento. Pero esto no sería un inconveniente, sino todo lo contrario, si hay varias imágenes y hay que recorrerlas todas. (Sí, bueno, lo vemos después).

Otro detalle importante es que la clase se reescribe completa, como una cadena. Por eso van a ver que además de la correspondiente a su orientación, en el valor también está puesta la original, porque en este caso ya la conocemos y no se justifica usar classList.add("").

Recordemos que className = "" no agrega otro valor, sino que reemplaza todo el contenido del atributo class="".

No usé una tercera función para el cambio de ruta, metí todo en el mismo valor de onclick="contempla.src=this.src; cambiaClase()". Y esa es otra ventaja de poner javascript en el head: la variable contempla ya existe para cuando hacemos el click y nos ahorra repetir código; después llamamos a la función que mide para cambiar la clase, y todo el mecanismo funciona.

3ª versión: Actuación múltiple

Ahora veamos una última versión. En este caso ya no vamos a cambiar formatos del abuelo sino de los padres, porque tenemos varios, cada uno con su respectiva imagen hija.

Lo que hace es recorrerlas una a una y confirmar si es horizontal, vertical o cuadrada, para asignar una clase a su contenedor dependiendo del caso.

Sigo usando código viejo y por lo tanto más compatible. Como sé que nadie lo va a copiar y pegar sino que lo van a estudiar y reescribir según sus necesidades, el modo en que haga funcionar el escript no necesita ser de última generación; nada más tiene que ser comprensible. Y para eso están los comentarios y los artículos anteriores. Y sobre todo, los buscadores y manuales de javascript donde van a encontrar la ampliación de cualquier dato.

Aquí la demo.

See the Pen Clase según alto y ancho de imagen.(3) by Kseso (@Kseso) on CodePen.

Como habrán notado, sigo insistiendo con disparar todo desde un onload dentro del bloque script. Sin dudas para estos casos más complejos es la mejor opción, pero los problemas empiezan cuando tenemos que poner en un documento varios escripts, y todos tienen su función disparada con onload. El evento puede reconocer más de una, pero no lo aceptan todos los navegadores.

onload = uno(), dos(), tres();

Hay métodos para salvar esto, y están en cualquier manual o respondidos con ejemplos en todos los foros especializados, pero ya que estoy, y viendo que este artículo me quedó medio pobre, les cuento que la solución que más me gusta es meter todas las funciones a ejecutar en una "función madre" y a ésta sí dispararla con el evento.

Por ejemplo, si tengo esas 3 funciones las puedo poner así y anda hasta en IExplorer 8.

<script type="text/javascript"> function uno() {alert("uno"); } function dos() {alert("dos"); } function tres() {alert("tres"); } onload = function(){ uno(); dos(); tres(); }; </script>

Autoría

Furoya: Autor del artículo

Artículo original de Furoya.
La intención del autor con sus colaboraciones no es que los artículos sirvan para hacer un copy&paste de códigos sino que comprendas y aprendas la lógica y el cómo trabaja javaScript.
Y a partir de lo expuesto experimentes tú.
El autor del post y el editor del blog te animamos a que plantees tus dudas o reflexiones y que compartas tus realizaciones en base a lo expuesto en los comentarios. Recuerda que puedes incluir pens (ejemplos en Codepen.io) en ellos.

Comentarios: 3

  1. Me hiciste recordar que no mencioné en el artículo tus versiones CSS puro publicadas hace ya un tiempo.

    Emular selector Css de precedente con position.

    Sé que no es lo mismo, pero es un antecedente ineludible.
    Mis disculpas; aunque por mi edad, seguro volverá a pasar.

    ;-)

    ResponderEliminar
    Respuestas
    1. Es que ni son ni se parecen.
      A día de hoy Css no puede (ni hacer ni emular) comparar dos variables de un elemento (como la relación entre anchura / altura) y en función del resultado aplicar una regla u otra.

      Así que de haber hecho mención hubiese sido poco acertada (y el editor posiblemente la hubiese eliminado xD xD

      Un saludo

      Eliminar
    2. Bien, sí, eso sí. Yo lo asociaba por el hecho de leer un evento o cambio en un nieto que modifique el formato de su padre o abuelo. Es algo típico de javascript que algún día podrá hacerse en CSS, pero de momento tenemos los "trucos" presentados en esa recopilación que enlacé.

      Aprovecho el comentario para agregar que la función anónima que incluye a otras en el 'onload' funciona hasta en IE6. Por si a alguien aún le interesa.

      Otro saludo.

      Eliminar

EsCss RSS del Blog RSSS Comentarios Humans.txt ᛯ Diseno por Kseso SiteMap