Javascript: Emular los métodos de classList mediante un objeto Set

Esto nos permitirá trabajar con un atributo de usuario como si se tratase del atributo class de una etiqueta

Clases de una etiqueta en HTML y CSS

Una etiqueta HTML puede pertenecer a ninguna, una o varias clases a la vez. Las clases se indican en el atributo class de la etiqueta.

<p class="🍐pera 🍓fresa 🥝kiwi" > … </p>

Si hay varias clases hay que separarlas con el espacio en blanco, como si de palabras se tratase. La etiqueta se mostrará con la combinación de estilos de todas las clases a la vez.

Las clases de una etiqueta son un conjunto de 0 o más elementos sin repetición

Uso de clases en Javascript

En Javascript podemos modificar las clases a las que pertenece un elemento mediante la propiedad .className o bien mediante la propiedad .classList

La propiedad .className es un texto directamente equivalente al atributo class de una etiqueta.

  • elemento.className = '🍐pera 🍓fresa 🥝kiwi'

La propiedad .classList nos ofrece la manera idónea de trabajar con clases ya que permite realizar una serie de operaciones muy comunes:

  • elemento.classList.contains('🍓fresa') — nos indica si el elemento contiene la clase 'fresa'
  • elemento.classList.add('🍇uva') — añade la clase 'uva' si no estaba ya presente
  • elemento.classList.remove('🍐pera') — borra la clase 'pera' si la tenía puesta
  • elemento.classList.toggle('🍓fresa') — añade la clase 'fresa' si no estaba o la borra si estaba.
  • elemento.classList.toggle('🍓fresa', false) — equivalente al método remove
  • elemento.classList.toggle('🍓fresa', true) — equivalente al método add
  • elemento.classList.replace('🥝kiwi', '🍊naranja') — reemplaza la clase 'kiwi' por la clase 'naranja'

Documentación MDN: Element.classList

El objeto Set

Realiza operaciones sobre un conjunto de elementos de manera similar a los métodos de .classList

  • const set = new Set(['🍐pera', '🍓fresa', '🥝kiwi']) — crea un conjunto de tres elementos
  • set.has('🍓fresa') — da cierto si existe el elemento
  • set.add('🍇uva') — añade un nuevo elemento si no existía antes
  • set.delete('🍐pera') — elimina el elemento

Documentación MDN: Objeto Set

Set vs .classList

Los métodos del objeto Set y los métodos de la propiedad .classList se utilizan para operaciones sobre un conjunto de datos. Pero mientras que Set maneja elementos de cualquier tipo, .classList maneja únicamente elementos de tipo texto

Objetivo

Crear un código que funcione igual que los métodos de .classList con la ayuda del objeto Set

Cada elemento del conjunto es una palabra: un texto sin espacios

Tanto el conjunto de entrada como el conjunto resultante será un texto de palabras separadas por espacio en blanco. Equivalente a como funciona el atributo class de una etiqueta

Código fuente

function Words (words) {
    const set = new Set(words.split(' '));
    return {
        contains: word => set.has(word),
        add: word => {
            set.add(word);
            return toWords();
        },
        remove: word => {
            set.delete(word);
            return toWords();
        },
        toggle: (word, force=undefined) => {
            const ok = force==undefined? !set.has(word) : force;
            if (ok) set.add(word); else set.delete(word);
            return toWords();
        },
        replace: (oldWord, newWord) => {
            if (set.has(oldWord)) {
                set.delete(oldWord);
                set.add(newWord);
            }
            return toWords();
        },
        get value() { return toWords(); },
    };
    function toWords() { return [...set].join(' ').trim(); }
}

Comparación de uso entre .classList y mi función Words

Inicialización

const elemento = document.querySelector('p');
const frutas = Words(elemento.className);

Comparativa

  if (elemento.classList.contains('🍓fresa')) …
  if (frutas.contains('🍓fresa')) …

  elemento.classList.add('🍇uva');
  elemento.className = frutas.add('🍇uva');

  elemento.classList.remove('🍐pera');
  elemento.className = frutas.remove('🍐pera');

  elemento.classList.toggle('🍓fresa');
  elemento.className = frutas.toggle('🍓fresa');

  elemento.classList.replace('🍇uva','🥝kiwi');
  elemento.className = frutas.replace('🍇uva','🥝kiwi');

Emular el atributo class

Mediante mi función Words podemos emular el atributo class de una etiqueta en otro atributo definido por usuario. A modo de ejemplo, si tenemos el atributo definido por usuario frutero:

<p data-frutero="🍐pera 🍓fresa 🥝kiwi" > … </p>

Podemos trabajar en Javascript como si fuese un atributo class así:

const elemento = document.querySelector('p');
const frutas = Words(elemento.dataset.frutero || '');

if (frutas.contains('🍓fresa')) …
elemento.dataset.frutero = frutas.add('🍇uva');
elemento.dataset.frutero = frutas.remove('🍐pera');
elemento.dataset.frutero = frutas.toggle('🍓fresa');
elemento.dataset.frutero = frutas.replace('🍇uva','🥝kiwi');

O bien así:

const elemento = document.querySelector('p');
const frutas = Words(elemento.dataset.frutero || '');

if (frutas.contains('🍓fresa')) …
frutas.add('🍇uva');
frutas.remove('🍐pera');
frutas.toggle('🍓fresa');
frutas.replace('🍇uva','🥝kiwi');

elemento.dataset.frutero = frutas.value;

CSS: emular el uso de clases mediante atributo de usuario

Clase CSSAtributo contiene palabra
.🍐pera { … }[data-frutero~="🍐pera"] { … }
.🍓fresa { … }[data-frutero~="🍓fresa"] { … }
.🥝kiwi { … }[data-frutero~="🥝kiwi"] { … }

De esta forma podemos liberar el uso del atributo class para diseño y tener la misma funcionalidad implementada con un atributo de usuario

Comentarios

Proinf.net, ©2003-2019 ci 3.1.10 (CC) Esta obra está bajo una licencia de Creative Commons Este software está sujeto a la CC-GNU GPL