Estilos dinamicos - manipulando CSS con JavaScript

By Chris Mills

Introducción

En este punto de la sección de JavaScript del curso, ya hemos cubierto los conceptos básicos del uso real de JavaScript, mirado cómo manejar elementos usando el árbol DOM, y visto cómo manipularlos una vez que los hemos localizado correctamente.

En este artículo echaremos un vistazo a cómo actualizar dinámicamente el estilo aplicado a los elementos mediante la manipulación de las plantillas CSS en tiempo de ejecución utilizando JavaScript. Para esto utilizaremos el mismo tipo de técnicas que ya hemos visto, pero tendremos que tener en mente una serie de consideraciones especiales cuando trabajemos con las plantillas CSS. Todo esto quedará cubierto mediante las siguientes secciones:

Accediendo a las hojas de estilos

El navegador proporciona una interfaz para interactuar con las hojas de estilos — en nuestro código JavaScript podemos acceder a una lista de nuestras hojas de estilos mediante el uso de document.styleSheets. document.styleSheets nos devolverá una lista de todas las hojas de estilo aplicadas a una página, incluyendo hojas de estilos externas referenciadas mediante un elemento link, y hojas de estilos internas que residan dentro de elementos style. Si nuestros elementos style tienen el atributo id, entonces podremos referenciarlos rápidamente mediante document.getElementById(element_id).

Además podemos añadir nuevas hojas de estilo a la página — podemos usar la función document.createElement para crear un nuevo elemento style. Esto es interesante cuando queremos dar a los visitantes de nuestra página la opción de cambiar los estilos de nuestra página dinámicamente, utilizando quizás algún botón. A continuación se presenta un pequeño ejemplo de cómo se podría crear una nueva hoja de estilos:

var hoja = document.createElement('style')
hoja.innerHTML = "div {border: 2px solid black; background-color: blue;}";
document.body.appendChild(hoja);

Eliminar una hoja de estilos es también bastante simple. Primero tenemos que obtener la hoja de estilos que queremos eliminar. Podemos hacer esto usando document.getElementById, como se muestra en un pequeño ejemplo anterior. Para eliminar la hoja de estilos, podemos usar la función del árbol DOM parent.removeChild(element), donde element es el objeto hoja de estilos que queremos eliminar, y parent es el nodo padre de nuestra hoja de estilos. Como hemos visto antes, para eliminar la hoja de estilos (hoja_a_eliminar) primero tenemos que obtener la referencia a su padre — var padre = hoja_a_eliminar.parentNode — después tenemos que llamar a removeChild con el parámetro de hoja_a_eliminar - padre.removeChild(hoja_a_eliminar)

var hoja_a_eliminar = document.getElementById('idHojaEstilos');
var padre = hoja_a_eliminar.parentNode;
padre.removeChild(hoja_a_eliminar);

El ejemplo de acceso a las hojas de estilos demuestra tanto el acceso a todas las hojas de estilos como la adicción y eliminación de nuevas hojas de estilos a la página.

Propiedades de las hojas de estilos

El objeto stylesheet está disponible a través de JavaScript, y nos permite acceder a información acerca de las hojas de estilos referenciadas desde la página actual, por ejemplo, si la hoja de estilos está desactivada, su localización, y la lista de reglas de CSS que contiene. Para una lista completa de las propiedades del objeto stylesheet (y otras cosas además), se puede revisar la documentación W3C sobre las hojas de estilo del árbol DOM.

Consideremos un ejemplo (por ahora) teórico — digamos que tenemos un sitio web donde mostramos una serie de artículos técnicos. Nosotros deseamos poner el foco en algunos de esos artículos con un bonito carrusel, pero tenemos el problema de los usuarios que no tienen activado JavaScript por algún motivo. Recordando las lecciones aprendidas en los principios de un JavaScript discreto, queremos que la funcionalidad del sitio web continúe funcionando incluso para esos usuarios, pero deseamos estilizar nuestro sitio web diferente para esos usuarios, de tal forma que su experiencia de usuario sea placentera, aún sin el carrusel.

Lo que podemos necesitar es una hoja de estilos que sólamente estará activa si JavaScript lo está. Estamos de suerte — el interfaz del árbol DOM para las hojas de estilo nos da acceso al atributo disabled, que nos permite activar o desactivar las hojas de estilos.

Muchas de las propiedades del objeto stylesheet son de sólo lectura, pero algunas, como disabled, no.

Además podemos usar las propiedades de las hojas de estilos para ayudarnos a diferenciar entre múltiples hojas de estilos en la página. La propiedad src nos ayuda a identificar hojas de estilos externas, pero no nos ayuda a referenciar elementos de estilo internos. Una forma mejor, que nos permite tener referencias a hojas de estilos individuales, tanto internas como externas, es usar la propiedad title. Si recorremos document.styleSheets podemos ver las diferentes hojas de estilos que hemos incluido en la página. El siguiente ejemplo nos muestra como podemos hacer ese recorrido:

function getStyleSheet(tituloUnico) {
  for(var i=0; i<document.styleSheets.length; i++) {
    var hojaEstilos = document.styleSheets[i];
    if(hojaEstilos.title == tituloUnico) {
      return hojaEstilos;
    }
  }
}

Para cada objeto stylesheet recuperado del array styleSheets podemos acceder a su propiedad title para comprobar si es el título que estamos buscando. Se puede ver un ejemplo funcional de esto en el ejemplo sobre añadir y eliminar reglas, que discutiremos en la próxima sección.

Cambiar entre diferentes hojas de estilos basado en la preferencia de usuario es una facilidad bastante común entre los sitios web — utilizando lo que hemos presentado hasta aquí, podemos tener múltiples hojas de estilos y activar solamente aquella que el usuario actual desee ver. Podemos ver eso en un ejemplo real — inicialmente el texto tiene estilo, pero cuando cambiamos el atributo disabled a true, nuestra CSS queda desactivada. Podemos tener activa nuestra CSS de nuevo poniendo disabled a false. Esto se puede comprobar en el ejemplo de propiedades de las hojas de estilos, para un rápido vistazo de como se puede usar todo esto.

Añadiendo y eliminando reglas

¿Recordamos el ejemplo teórico de un sitio web que discutimos más arriba? Recordemos que ese sitio tenía una lista de artículos; algunos eran sobre CSS, otros eran sobre HTML, y otros sobre JavaScript. En nuestra página web mostramos todos los artículos, pero nuestro usuario sólo desea ver los artículos sobre CSS. ¿Cómo podemos hacer esto? Porque todos los artículos se están mostrando ya, y no queremos tener que pedir al servidor una página específica conteniendo justo los artículos sobre CSS — eso es una pérdida de tiempo.

Para solucionar este problema, podemos usar JavaScript para recorrer todos los artículos y hacer que solamente los artículos sobre CSS sean visible (discutiremos sobre cómo hacer esto mas tarde), ó añadir una regla a una de nuestras hojas de estilo que hará que sólo los artículos sobre CSS sean visibles. Utilizar CSS será más rápido que recorrer todos los elementos uno a uno.

El objeto stylesheet dispone de dos funciones para ayudarnos con este problema. La primera es la función insertRule, algo así como esto:

stylesheet.insertRule(regla, indice)

regla es una cadena conteniendo la regla que queremos añadir a la hoja de estilos. indice especifica dentro de la lista de reglas de la hoja de estilos, dónde hay que que poner esa regla. A continuación un ejemplo:

hojaEstilos.insertRule(".have-border { border: 1px solid black;}", 0);

Existe un ejemplo donde se demuestra el uso de la función insertRule. En ese ejemplo existe una lista de todas las reglas en una hoja de estilos. Cuando presionamos el botón, añade una regla con el índice 2 que hace que el texto se vuelva rojo, añadiendo la propiedad color: red a la regla p { ... } rule. Se puede echar un vistazo a esto en el ejemplo sobre añadir y eliminar reglas.

Si deseamos eliminar esa regla, podemos llamar a la función stylesheet.deleteRule(indice), donde indice es el índice de la regla que queremos quitar.

En el ejemplo de este artículo, podemos crear una regla que cambie display a none para todo el HTML y los artículos de JavaScript — podemos ver esto en acción en el ejemplo del carrusel.

Advertencia: IE no implementa las reglas de acuerdo con el estandar. En vez del atributo cssRules él utiliza rules. IE además usa removeRule en vez de deleteRule y addRule(selector, regla, indice) en vez de insertRule.

Cambiando elementos Style

En este punto ya deberiamos entender como editar las páginas de estilos asociadas a una página, y crear y modificar las reglas CSS en ella. ¿Y que pasa si lo que queremos es modificar un elemento específico dentro del árbol DOM?. Utilizando el API del árbol DOM podemos acceder a un elemento específico de la página. Si echamos la vista atrás, en el ejemplo del carrusel, la funcionalidad se define de tal forma que cuando nosotros hacemos click sobre un artículo, el artículo se resalta mientras que el texto del artículo se muestra debajo.

A través del árbol DOM, podemos acceder al objeto style que define el estilo de un elemento. Este objeto style se define como CSSStyleDeclaration; se puede ver una explicación detallada de esto en la documentación W3C del interfaz CSSStyleDeclaration. El objeto style no funciona como otras propiedades definidas en los elementos HTML. Es diferente, mientras element.href ó element.id devuelven cadenas, element.style devuelve un objeto. Esto significa que no se puede establecer un estilo simplemente asignando una cadena a element.style.

El objeto style tiene atributos que se corresponden con las diferentes propiedades CSS que podemos establecer. Por ejemplo, style.color devuelve el color de un elemento. Si llamamos a element.style.color = "red"; podemos aplicar ese cambio de estilo dinámicamente. A continuacion hay una función que establece el color de un elemento a rojo cuando le pasamos el id del elemento.

function colorElementRed(id) {
  var element = document.getElementById(id);
  element.style.color = "red";
}

También podemos utilizar setAttribute(key, value) para establecer el estilo de un elemento. Por ejemplo, podemos cambiar a rojo el color de un elemento invocando a element.setAttribute('style','color: red');, pero debemos ser precavidos, porque esto borrará cualquier cambio anterior que hayamos hecho al objeto style

Cuando establecemos el estilo de un elemento de esta forma, es lo mismo que si lo estuvieramos asignando en la declaracion del atributo style en el elemento html. El estilo sólo se se aplicará si la importancia y la especificación de la regla es mayor que la de otras reglas aplicadas al elemento (la especificación se explica en un artículo anterior, la herencia y la cascada en CSS.

Algunos de nosotros ya estaremos preguntandonos que ocurre cuando la propiedad CSS a la que pretendemos dar valor tiene nombre compuesto. La sintaxis aquí es diferente, porque, por ejemplo, si escribimos element.style.font-size, JavaScript entenderá que debe restar size de element.style.font, que no es lo que deseamos que ocurra. Para evitar que esto ocurra, todas las propiedades CSS se escriben como en el siguiente ejemplo, donde escribimos element.style.fontSize para acceder al tamaño de la fuente:

function changeElement(id) {
  var element = document.getElementById(id);
  element.style.color = "red";
  element.style.fontSize = "15px";
  element.style.backgroundColor = "#FFFFFF";
}

¿Recordamos los objetos stylesheet?. Bien, styleSheet.cssRules nos dará una lista de todos los objetos style representando todas las reglas CSS contenidas en la página de estilos. Se puede modificar estos objetos style como el objeto style de otros elementos. En este caso, mñas que aplicar cambios sobre un elemento específico de la página, los cambios afectarán a todos los elementos que se vean afectados por esa regla.

En el ejemplo que viene a continuación, la función que aumenta el tamaño de la letra utiliza el objeto style y la función que hace el tamaño más pequeño utiliza setAttribute. Si nosotros cambiamos el color del texto a rojo, y entonces llamamos a setAttribute mediante el botón apropiado, veremos como nuestros cambios se sobreescriben. Se puede ver en el ejemplo de cambio de estilos.

Nombres de clases de elementos

Otra forma de alterar el estilo de un elemento es cambiando su atributo class. class es una palabra reservada en JavaScript, asi que para acceder al atributo class de un elemento tenemos que usar element.className. Se pueden concatenar cadenas a className si deseamos añadir una clase a un elemento, o podemos sobreescribir className y asignarle una clase nueva. Esto lo podemos ver en el ejemplo sobre nombres de clases.

Resúmen

Conocer como cambiar dinámicamente los estilos aplicados a nuestras páginas es muy útil para construir modernas, ricas e interactivas páginas web; el conocimiento presentado en este artículo es justo la base de técnicas más avazandas como animaciones JavaScript. Así, debemos tener cuidado de usar las modificaciones de estilos con responsabilidad, y no utilizarlas excesivamente. También, como se dice más arriba, las modificaciones de estilos pueden mejorar el rendimiento, mostrando y ocultando contenido que puede ayudar a no tener que invocar al servidor bajo ciertas circunstancias.

Ejercicios

  • ¿Cual es la diferencia entre setAttribute y establecer el estilo a través del objeto CSSStyleDeclaration?
  • Enumera dos formas de conseguir que todas las imágenes tengan un borde verde cuando un usuario haga click sobre un botón.
  • ¿Cambiando el objeto CSSStyleDeclaration de un elemento se cambia siempre el estilo del elemento? ¿Porqué?
  • Enumera dos formas de acceder a una hoja de estilo específica.

Sobre el autor

Picture of the article author Greg Schechter

Greg Schechter es bastante joven en la industria tecnológica sobre webs — él es actualmente un estudiante de informática (computer science) en la University of Illinois in Urbana Champaign. El es muy entusiasta en entrar en la industria de aplicaciones webs, donde él espera construir sitios web prácticos y ricos para usuarios en todos el mundo. Se puede encontrar mas detalles de Greg en GregTheBusker.com.

Chris Mills is a web technologist, open standards evangelist and education agitator, currently working at Opera Software in the developer relations team. He spends most of his time writing articles about web standards for dev.opera.com and other publications (such as .net mag and A List Apart), giving talks at universities and industry conferences, and lobbying universities to improve their web education courses. He believes that education is the answer to everything, but in particular he is passionate about using education to improve the overall content quality, accessibility, usability and future-viability of the Web.

He is the creator of the Opera Web standards curriculum, contributor to the WaSP InterACT project, and coauthor of InterACT with web standards: A Holistic Approach to Web Design. In August 2011, he also accepted the position of co-chair of the newly-formed Web Education Community Group.

Outside work he is a heavy metal drummer, proud father of three and lover of good beer.


This article is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported license.

Comments

No new comments accepted.