
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!