- Cash es una biblioteca alternativa a jQuery muy pequeña que ofrece una sintaxis estilo jQuery para manipular el DOM en navegadores modernos con soporte para IE11+
- Aprovecha funciones de los navegadores modernos para reducir la base de código y permitir usar los conocidos métodos encadenables con un tamaño de archivo mucho menor
- No busca una equivalencia funcional del 100% con jQuery, pero cubre la mayoría de los casos de uso cotidianos y la API implementada es en gran medida compatible con jQuery
- Su tamaño es de 6 KB minificado y comprimido con gzip, un 76.6% más pequeño que los 24.4 KB de jQuery Slim 3.4.1, y puede reducirse aún más con compilaciones parciales
- Ofrece una base de código en TypeScript, tipos de TypeScript generados desde el código, eventos con espacio de nombres y soporte para compilaciones parciales que permiten excluir métodos individuales
El problema que resuelve Cash
- Cash es una alternativa a jQuery para navegadores modernos que proporciona el selector
$() al estilo jQuery y métodos de colección encadenables para manipular el DOM
- El soporte está dirigido a navegadores IE11+
- No tiene como objetivo implementar todas las funciones de jQuery tal cual, pero las funciones que Cash sí implementa están diseñadas para ser en su mayoría compatibles con la API de jQuery
- Los usuarios que migren desde jQuery pueden consultar la migration guide
Comparación de tamaño y funciones
- En la comparación de tamaño de archivo, Cash es más pequeño que Zepto 1.2.0 y jQuery Slim 3.4.1
- Sin minificar: 36.5KB
- Minificado: 16KB
- Minificado y con gzip: 6KB
- jQuery Slim 3.4.1 pesa 24.4KB minificado y con gzip, y Cash representa una reducción de tamaño del 76.6% frente a este
- Si se necesita un paquete aún más pequeño, se pueden usar partial builds
- En comparación de funciones, Cash ofrece soporte para navegadores modernos, mantenimiento activo, eventos con espacio de nombres, base de código en TypeScript y tipos de TypeScript generados desde el código
- En Cash, las compilaciones parciales permiten excluir métodos individuales, mientras que en Zepto y jQuery Slim se indica exclusión por módulos completos
Cómo usarlo
- Cash puede cargarse desde jsDelivr y usarse directamente en el navegador
<script src="https://cdn.jsdelivr.net/npm/cash-dom/…;
<script>
$(function () {
$('html').addClass ( 'dom-loaded' );
$('<footer>Appended with Cash</footer>').appendTo ( document.body );
});
</script>
- El paquete de npm se ofrece con el nombre
cash-dom
npm install --save cash-dom
import $ from "cash-dom";
$(function () {
$('html').addClass ( 'dom-loaded' );
$('<footer>Appended with Cash</footer>').appendTo ( document.body );
});
Estructura de la API
$() es el método selector central de Cash y devuelve una colección de nodos manipulables
- Si se le pasa una función, ejecuta esa función cuando el DOM está listo
$() puede recibir un selector, un nodo del DOM, una nodeList, una cadena HTML, una colección de Cash o un callback de document ready
- Cash ofrece en términos generales tres tipos de API
- Selectores de consulta
- Métodos de colección
- Métodos de biblioteca del objeto global
$
Métodos de colección
- Los métodos de colección se llaman después de crear una colección con
$(), de una forma como $(element).addClass(className)
- Las categorías disponibles se dividen en atributos, colección, CSS, datos, dimensiones, efectos, eventos, formularios, manipulación del DOM, offset y recorrido
- Entre los métodos principales se incluyen los siguientes
- Clase/atributo:
addClass, removeClass, toggleClass, attr, prop, removeAttr
- Manejo de colecciones:
add, each, eq, filter, first, get, map, slice
- Manipulación del DOM:
append, prepend, before, after, html, text, clone, remove, replaceWith, wrap
- Eventos:
on, off, one, ready, trigger
- Recorrido:
find, children, closest, parent, parents, siblings, next, prev
- Se ofrecen algunos extra methods, pero están desactivados por defecto
$.fn es el prototipo principal de la colección, y permite agregar métodos personalizados a todas las colecciones al estilo de un plugin
Métodos globales de Cash
- El objeto global
$ incluye métodos de verificación de tipos y utilidades
- Los métodos de verificación de tipos ofrecen
$.isArray, $.isFunction, $.isNumeric, $.isPlainObject, $.isWindow
- Entre las utilidades se incluyen
$.guid, $.each, $.extend, $.parseHTML, $.unique
$.extend amplía el objeto de destino con propiedades del objeto fuente, y también soporta extensión profunda
$.parseHTML devuelve una colección a partir de una cadena HTML, y $.unique devuelve un nuevo arreglo sin duplicados
Extensión y contribución
- Cash puede ampliarse con métodos personalizados, y la forma de hacerlo está resumida en extending Cash
- Los problemas o solicitudes de funciones pueden abrirse como issue en GitHub
- El flujo de trabajo para pull requests consiste en clonar el repositorio, instalar dependencias, recompilar automáticamente con
npm run dev, ejecutar pruebas con npm run test y actualizar el README si hace falta
- La licencia es MIT
1 comentarios
Opiniones de Hacker News
Como los navegadores de hoy han mejorado, para simplificar la manipulación del DOM muchas veces bastan estos alias de dos líneas
dqs = document.querySelector.bind(document);dqsA = document.querySelectorAll.bind(document);Así se puede usar
dqs('#country')en lugar dedocument.querySelector('#country'), odqsA('.city')en lugar dedocument.querySelectorAll('.city')Para lo demás está bien usar simplemente funciones nativas del navegador, y normalmente se importan desde un módulo, como
import { dqs, dqsA } from '/lib/js/dqs.js';https://github.com/no-gravity/dqs.js
querySelector/querySelectorAllen sí parecen útiles y razonables, pero traerlas conimportes excesivoSon apenas dos líneas simples, no hay razón para ponerlas como dependencia; basta con copiarlas y pegarlas
Por ejemplo, para manejar varios eventos,
$.fn.one()o$.fn.on()son más fáciles de usar con jQuery/Cash, y si se mira la implementación interna hay bastante trabajo: https://github.com/fabiospampinato/cash/blob/master/src/even...querySelectorAll()no es una colección live, así que muchas veces se convierte directamente en arreglo asídqsA = s => Array.from(document.querySelectorAll(s));De esta forma se pueden usar de inmediato métodos de arreglo como
.map()o.filter()sobre el resultado, lo que mantiene una sensación parecida a jQueryPor ejemplo, durante las pruebas en Safari el evento
selectde cierto elemento directamente no se disparaba, y en algunos navegadores no había evento cuando solo se movía el caretAunque no necesitara funciones de React como componentes, estado o props, las funciones nativas del navegador no bastaban, y quedó claro el valor de React DOM para cubrir las diferencias entre navegadores
Después de quitar casi todos los polyfills, creo que la ventaja duradera que le queda a jQuery es el procesamiento automático de listas
La capacidad de desmarcar con una sola llamada todos los botones dentro de un formulario todavía es difícil de igualar en otros lados, y lo mismo pasa con las consultas al padre
Pero el mayor problema de la implementación es que, cuando la lista está vacía, falla silenciosamente
Como ya corregí demasiados bugs de este tipo al refactorizar después el árbol DOM con fines de layout, si hoy volviera a hacer jQuery, por defecto lanzaría un error con conjuntos vacíos, y dejaría una llamada encadenada o un flag para fallar silenciosamente solo cuando realmente no importe
Hace tiempo pasé unas horas viendo si se podía sacar Sizzle y cambiarlo de esa forma, pero no seguí avanzando
Al final, jQuery también se conecta con el viejo debate de librería vs. framework, y después de pasar tanto tiempo haciendo apps de una sola página con frameworks enormes, siento que se acerca de nuevo el valle de la desilusión
Si le ordenas ocultar todos los
.foo, si existen los oculta y si no, no pasa nada: un enfoque fire and forget, parecido a CSSSi escribes
.foo { color: red; }y no hay.fooen el documento, aparte de un pequeño overhead no hay efectos secundariosdocument.querySelectorAll('input[type=checkbox]').forEach((i) => i.checked = false);Es una forma de aprovechar
NodeListiterable y helpers de iteradoresMuchas consultas al padre pueden resolverse con
element.closest()El código basado en jQuery exige que todos los desarrolladores conozcan todos los selectores del proyecto y los actualicen cuando cambie el DOM, algo que obviamente es imposible
Parece viable reemplazar la función de inicialización de jQuery por una implementación propia que fuerce la comprobación de longitud
En un contexto donde los sitios web mainstream reparten literalmente megabytes de JavaScript, no entiendo por qué habría que reescribir una librería completa con menos funciones solo para ahorrar 50 KB
Muchos artículos se oponen al uso de jQuery por el tamaño del paquete y las restricciones de ancho de banda, mientras al mismo tiempo defienden frameworks SPA que consumen muchísimo más ancho de banda
Es un razonamiento de culto cargo completamente absurdo
En los tres casos, la respuesta es la misma: lo grande no soy yo, y yo estoy haciendo otra cosa más pequeña
Otra respuesta es que, si algo demasiado grande es el problema, hacerse más pequeño suena como una solución
Al final, parece que lo que se quiere preguntar es por qué un desarrollador dedicaría tiempo a reescribir una librería, pero eso no es tan sorprendente
Una parte considerable de la programación consiste en reescribir cosas ya hechas, ya sea porque el trabajo lo requiere, porque se necesita un comportamiento o características de rendimiento un poco distintas, o simplemente porque se quiere aprender cómo funcionan
Si estás buscando una alternativa a jQuery, después de esperar demasiado por jQuery 4.0 terminé creando algo propio parecido a jQuery, con algunas diferencias importantes.
Para animaciones, tweens y timelines usa CSS puro en vez del sistema personalizado de jQuery; maneja de forma transparente elementos individuales y listas, y apunta más a usarse inline.
Dice que
me()devuelve 1 elemento, o el primer elemento, onull, y queany()devuelve un arreglo o un arreglo vacío.Pero los ejemplos de abajo, como
any('button')?.forEach(...)yany('button')?.map(...), implican que también podría sernull.Me confunde si
any()siempre devuelve un arreglo como dice la descripción anterior, o si también puede sernullcomo en los ejemplos de abajo.Me interesa especialmente la localidad del comportamiento.
Tengo curiosidad por saber qué experiencia han tenido usando
currentScript.parentElement.Cuando lo investigué rápidamente el mes pasado, me quedó la impresión de que quizá no era confiable en casos borde, aunque no recuerdo exactamente cuándo.
No lo investigué a fondo, y me alegra que hayan logrado que funcione bien.
Si asumimos que no es
asyncnimodule, me parece que incluso cargando 3 scripts consecutivos,currentScript.parentElementdebería seguir funcionando en todos los navegadores.SvelteKit también tuvo esta discusión y al final implementó IDs aleatorios para especificar el elemento de destino: https://github.com/sveltejs/kit/issues/2221
Revisando la guía de migración, aprendí algunas cosas que jQuery puede hacer y Cash no, que no conocía y que quizá algún día valga la pena probar.
https://github.com/fabiospampinato/cash/blob/master/docs/mig...
Como objetivo extendido, estaría bueno usar magia de template strings de TypeScript para inferir con precisión el tipo de elemento.
Por ejemplo, se podría inferir estáticamente que
$('div#name')es unHTMLDivElement.typed-query-selector.Hay un ejemplo de uso real aquí: https://github.com/GoogleChrome/lighthouse/blob/main/types/i...
No sé si eso es posible en TypeScript, y no veo bien cómo se podría hacer.
Escuché que jQuery 4 es una alternativa a jQuery para navegadores modernos.
Al principio usé esto en la extensión de navegador que estoy creando, pero al final me pasé a una biblioteca JSX.
Cuando pasas del terreno de las “apps simples”, jQuery se convierte rápidamente en código difícil de razonar, y lo digo incluso como alguien que creó una biblioteca inspirada en jQuery.
Al final hay que usar la herramienta adecuada para el trabajo.
[1]: https://github.com/aleclarson/dough
Si puedes manejar bien jQuery en apps medianas o grandes, perfecto, pero no es mi preferencia.
Antes, cuando intentaba reducir JS, usaba https://github.com/filamentgroup/shoestring.
La razón principal era que ofrecía builds personalizados con solo lo que realmente necesitabas.
Cash parece tener una función similar, aunque está un poco más escondida en la documentación: https://github.com/fabiospampinato/cash/blob/master/docs/par...
Si lo usara, creo que intentaría primero por ese lado.
Aun así, sigo pensando que hoy es mejor opción usar directamente lo que ya ofrecen los navegadores.
En la práctica es bastante bueno, y jQuery ya no es imprescindible.
Más aún si consideramos que incluso una alternativa pequeña a jQuery pesa 6 kB, mientras que Preact, una biblioteca similar a React, pesa la mitad.
No estoy seguro de que ayude mucho más que ponerles alias a las Web APIs que ya existen.