Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

?id=b53cf04d-8919-4745-b4d1-0dec48e77da4

jQuery avanzado: crear pseudoelementos propios para los selectores

Imagen ornamental

A pesar de los avances de los últimos años de HTML y CSS, jQuery sigue siendo una biblioteca extremadamente útil y todavía hoy la utilizan casi el 85% de los principales 6 millones de sitios web del mundo y su uso no ha parado de crecer.

Lo más básico es poder utilizarla para seleccionar cualquier conjunto de elementos de la página, empleando selectores muy parecidos a los de CSS, por ejemplo:

var elementos = $('#contenedor ul.lista>li:nth-child(2n)');

Esto seleccionará y posteriormente nos permitirá procesar los elementos de listas no ordenadas con la clase .list aplicada y que sean hijos de un elemento con identificador contenedor. Para lo cual se ha utilizado una expresión idéntica a la que usaríamos para un selector CSS.

De hecho, por debajo, si puede, utiliza los selectores CSS y el método querySelectorAll() nativo de JavaScript.

JQuery nos da mucho más que esto, pero hoy me quiero centrar en una cuestión poco conocida, única de jQuery frente a CSS, y que puede ser muy potente: crear pseudoelementos propios para usar en estos selectores.

Filtrando elementos

En jQuery siempre hemos podido filtrar un conjunto de resultados de manera sencilla usando el método filter(). La mayor pega de éste es que hay que crear la función de filtrado o conocer la función que nos interesa y acordarnos de aplicarla correctamente.

Por ejemplo, imagínate que quieres filtrar dentro de una lista de elementos para que te devuelva tan solo los que contengan enlaces que apuntan a la web de campusMVP y ponerles un color de fondo rojo. Podrías escribir algo como esto:

$('#enlaces li>a').filter(function() {
                return $(this).attr('href').toLowerCase().startsWith('https://www.campusmvp.es');
            }).css('background-color', 'red');

Lo que hace esto es que, tras seleccionar todos los enlaces que están en la lista llamada enlaces, se filtran llamando a una función anónima que lo único que hace es comprobar si la URL de los enlaces empieza con https://www.campusmvp.es para saber si apuntan a la Web de campusMVP. En caso afirmativo, éstos pasarán a la colección final y se les aplicará un color rojo al fondo.

Pero, en lugar de esto ¿no sería mucho más sencillo poder escribir algo como esto?:

$('#enlaces li>a:campusmvp').css('background-color', 'red');

Fíjate en que hemos usado un pseudoelemento personalizado llamado :campusmvp que lo que hace es lo mismo que lo anterior: selecciona sólo los enlaces que apuntan a campusMVP. Nos evita tener que saber cómo se obtienen y además crea un código más corto y más fácil de usar. Sólo tenemos que saber que existe ese pseudoelemento y aplicarlo. Mola mucho ¿o no?

Vamos a ver cómo crearlo...

Creando pseudoelementos personalizados

La forma de lograrlo está muy poco documentada, y de hecho, no conozco una página oficial donde lo expliquen (lo más parecido es la minidocumentación de Sizzle, la biblioteca que usa jQuery por debajo para las selecciones, y no es nada claro), pero está soportado desde hace mucho tiempo.

Consiste en extender con $.extend el operador : que va delante de los pseudoelementos, así:

$.extend($.expr[':'], objeto);

siendo objeto un objeto anónimo cuyas propiedades son los pseudoelementos que vamos a crear, y su valor asignado es la función que realizará el filtrado. Por ejemplo, para el caso anterior bastaría con hacer esto:

$.extend($.expr[':'], {
            campusmvp : function(elem, i, partes) {
                return elem.tagName == 'A' && $(elem).attr('href').toLowerCase().startsWith('https://www.campusmvp.es');
            }
    }
);

Fíjate en que el objeto (anónimo en este caso) tiene una propiedad campusmvp a la que se le asigna una función que devuelve true en caso de que el elemento sea un enlace y tenga un atributo href que apunta a la web de campusMVP. Se parece mucho a la función de filtro, pero en este caso toma tres argumentos, de los cuales el único indispensable es el primero (el nombre es lo de menos, lo que importa es la posición):

  • elem: el elemento del DOM que cumple con el selector anterior y al cual se le aplica la función para ver si satisface nuestros requisitos. Fíjate en que no es un elemento de jQuery, sino del DOM.
  • i: siempre recibe 0. No he logrado averiguar su utilidad, la verdad.
  • partes: obtiene una matriz con 4 elementos. Los dos primeros son el pseudoelemento utilizado (campusmvp en este caso), el tercero siempre va en blanco y el último (partes[3]) es el contenido de los paréntesis si lo hay (más sobre esto en breve, de momento olvídate).

Vale, definiendo esto de esta manera al principio (por ejemplo, en la carga de la página con $() o en un script previo a su uso) podemos usarlo en cualquier expresión como la que vimos antes:

var enlacesAcampusMVP = $('#enlaces li>a:campusmvp');

que obtendrá todos los enlaces hijos directos de un elemento de lista dentro del elemento con el identificador enlaces, y luego con :campusmvp los comprobará uno a uno para ver si cumplen con las condiciones. O sea, es muy parecido a realizar un filtro, pero mucho más claro y fácil de usar.

Las posibilidades de esto son inmensas. Por ejemplo, de la misma manera podemos crear un pseudoelemento llamado :externo para obtener todos los enlaces que sean externos a la web actual (cuyo dominio no coincida con el actual), para lo cual podríamos añadir esta propiedad al objeto anterior:

externo : function(elem) {
    return elem.href && elem.hostname && elem.hostname !== window.location.hostname;
}

y obtener todos los enlaces externos de la página sería tan fácil como:

var enlacesExternos = $('a:externo');

o usándolo como parte de cualquier otro selector más complejo.

Creando pseudoelementos con opciones

También es posible crear pseudoelementos que tomen opciones usando paréntesis, de modo parecido a cómo los utiliza el pseudoelemento :not de jQuery:

var resultado = $('input:not(:checked) + span');

que en este caso selecciona todos los span a continuación de cualquier control de input de tipo "check", que no esté seleccionado.

Pues nosotros podemos crear selectores en forma de pseudoelementos que tomen parámetros como en este caso.

Por ejemplo, imaginemos que queremos crear un pseudoselector que nos permita seleccionar elementos cuya caja ocupe menos, igual o más que un determinado ancho, para escribir selectores como este:

var cajas150 = $('#cajas div.caja:ancho(=150)')

que obtendrá todos los div con la clase .caja aplicada dentro del elemento con id #cajas que además tengan un ancho de exactamente 150 píxeles.

Para conseguirlo entra en juego el tercer parámetro que se le pasa a la función, del que hablábamos antes: partes. Si se utilizan paréntesis justo a continuación de nuestro pseudoelemento, entonces el contenido de lo que haya dentro aparece como cuarto elemento de esta matriz (sería partes[3] para obtenerlo).

Sabiendo esto, crear un pseudoelemento ancho como el que acabamos de describir al que le puedas pasar valores entre paréntesis es algo muy sencillo. En este ejemplo concreto el código sería:

ancho : function(elem, i, match) {
    var parametro = match[3];
    if (parametro == '')
        throw new Error('El pesudoelemento "ancho" debe tomar un parámetro');
    var operador = parametro.substr(0,1).trim(),
        valor = parametro.substr(1).trim();

    var ancho = $(elem).width();

    //Sólo he implementado tres operadores =, < y >
    switch (operador) {
        case "=":
            return valor == ancho;
            break;
        case "<":
            return ancho < valor;
            break;
        case ">":
            return ancho > valor;
            break;
        default:
            throw new Error('Sólo se soportan los operadores =, < y > para el ancho');
            break;
    }
}

Lo que se hace es obtener el valor entre paréntesis (parametro) ya en la primera línea. Como lleva un operador (<, = o >) en la primera posición, lo separamos y así tenemos por un lado el operador y por otro el valor (por ejemplo, = y 150). Luego tan solo se procesa con un switch para actuar en función del operador y el valor comprobando el ancho del elemento actual que se le pasa a la función y determinar así si cumple o no la condición. ¡Listo!

Ahora ya está preparado para ser utilizado y poder escribir, por ejemplo:

$('#cajas div.caja:ancho(<150)').css('background-color', 'yellow');

para que ponga todos los elementos que cumplan con el selector previo y tengan menos de 150 píxeles de ancho con color de fondo en amarillo.

No sólo eso, sino que podemos combinarlos y utilizarlos para hacer cosas más complejas como esta:

$('#cajas div.caja:ancho(>100):ancho(<150)').css('background-color', 'red');

que usa dos de estos pseudoelementos para seleccionar todas las cajas que cumplan con el selector y que tenga un ancho mayor de 100 pero menor de 150 y ponerles un fondo rojo 😲

En resumen

Como ves las posibilidades son enormes y podemos extender jQuery según nuestras necesidades para facilitar mucho el uso de condiciones complejas en los selectores.

Te dejo el ejemplo completo con los 3 pseudoelementos descritos en este artículo para que puedas probarlos y experimentar: campusMVP-jquery-pseudo-elementos-propios.zip (1,45 Kb).

¡Espero que te resulte útil!

José Manuel Alarcón Fundador de campusMVP, es ingeniero industrial y especialista en consultoría de empresa. Ha escrito diversos libros, habiendo publicado hasta la fecha cientos de artículos sobre informática e ingeniería en publicaciones especializadas. Microsoft lo ha reconocido como MVP (Most Valuable Professional) en desarrollo web desde el año 2004 hasta la actualidad. Puedes seguirlo en Twitter en @jm_alarcon o leer sus blog técnico o personal. Ver todos los posts de José Manuel Alarcón
Archivado en: Desarrollo Web

¿Te ha gustado este post?
Pues espera a ver nuestro boletín mensual...

Suscríbete a la newsletter

La mejor formación online para desarrolladores como tú

Comentarios (2) -

¡Muy bueno! No sabía que se podía hacer esto con los selectores de jQuery.

Responder

José Manuel Alarcón
José Manuel Alarcón

¡Hey Alberto! Cuánto tiempo sin saber de ti. Me alegro de verte por aquí y de que te parezca interesante.

Tenemos que vernos un día.

Saludos!

Responder

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.