Escribir una mejor biblioteca de JavaScript para el DOM

 

 

 

  • Implemente rápidamente. Implementar inteligentemente
  • Clase magistral de CSS moderno avanzado, con Manuel Matuzović

  • Índice
    1. Agregar funciones útiles a la biblioteca
      1. Extensiones en vivo
      2. Animaciones nativas
      3. Microplantillas integradas
      4. Apoyo a la Internacionalización
    2. Hacer que las API nativas sean más elegantes
      1. Captador y fijador
      2. Manejo de eventos mejorado
      3. Soporte de métodos funcionales
    3. Evitar problemas con jQuery
      1. La función $ “mágica”
      2. Valor del operador []
      3. Problemas con devolución falsa
      4. encontrar y encontrar todo
    4. Conclusión
      1. Otras lecturas

    Últimamente, Maksim Chemerisuk ha comenzado a ver más y más problemas con el núcleo de jQuery y no se pueden solucionar sin romper la compatibilidad con versiones anteriores. Él, como muchos otros, continuó usando la biblioteca por un tiempo, navegando por todas las molestas peculiaridades todos los días. Luego, Daniel Buchner creó SelectorListener y comenzó a pensar en crear un conjunto de funciones que permitieran la posibilidad de construir componentes DOM discretos utilizando un mejor enfoque.

     

    Actualmente, jQuery es la biblioteca de facto para trabajar con el modelo de objetos de documento (DOM). Se puede utilizar con marcos MV* populares del lado del cliente (como Backbone) y tiene un montón de complementos y una comunidad muy grande. A medida que el interés de los desarrolladores en JavaScript aumenta minuto a minuto, mucha gente siente curiosidad por saber cómo funcionan realmente las API nativas y cuándo podemos usarlas en lugar de incluir una biblioteca adicional.

    Últimamente he empezado a ver más y más problemas con jQuery, al menos con mi uso. La mayoría de los problemas están relacionados con el núcleo de jQuery y no se pueden solucionar sin romper la compatibilidad con versiones anteriores, lo cual es muy importante. Yo, como muchos otros, seguí usando la biblioteca por un tiempo, navegando por todas las molestas peculiaridades todos los días.

    Luego, Daniel Buchner creó SelectorListener y se manifestó la idea de "extensiones en vivo" . Empecé a pensar en crear un conjunto de funciones que nos permitieran construir componentes DOM discretos utilizando un enfoque mejor que el que hemos usado hasta ahora. El objetivo era revisar las API y soluciones existentes y crear una biblioteca más clara, comprobable y liviana.

    Agregar funciones útiles a la biblioteca

    La idea de las extensiones en vivo me animó a desarrollar el proyecto Better-Dom , aunque otras características interesantes hacen que la biblioteca sea única. Repasémoslos rápidamente:

    • extensiones en vivo
    • animaciones nativas
    • microplantillas integradas
    • apoyo a la internacionalización

    Extensiones en vivo

    jQuery tiene un concepto llamado "eventos en vivo". Basándose en la idea de delegación de eventos, permite a los desarrolladores manejar elementos existentes y futuros. Pero en muchos casos se requiere más flexibilidad. Por ejemplo, los eventos delegados se quedan cortos cuando es necesario mutar el DOM para inicializar un widget. De ahí las extensiones en vivo.

    El objetivo es definir una extensión una vez y hacer que los elementos futuros se ejecuten a través de la función de inicialización, independientemente de la complejidad del widget. Esto es importante porque nos permite escribir páginas web de forma declarativa; Por lo tanto, funciona muy bien con aplicaciones AJAX.


    Las extensiones en vivo le permiten manejar cualquier elemento futuro sin la necesidad de invocar la función de inicialización. ( Créditos de la imagen )

    Veamos un ejemplo sencillo. Digamos que nuestra tarea es implementar una información sobre herramientas totalmente personalizable. El :hoverpseudo-selector no nos ayudará aquí porque la posición de la información sobre herramientas cambia con el cursor del mouse. La delegación de eventos tampoco encaja bien; Escuchar mouseovery mouseleaveescuchar todos los elementos del árbol de documentos es muy costoso. ¡Extensiones en vivo al rescate!

     

    DOM.extend("[title]", { constructor: function() { var tooltip = DOM.create("span.custom-title"); // set the title's textContent and hide it initially tooltip.set("textContent", this.get("title")).hide(); this // remove legacy title .set("title", null) // store reference for quicker access .data("tooltip", tooltip) // register event handlers .on("mouseenter", this.onMouseEnter, ["clientX", "clientY"]) .on("mouseleave", this.onMouseLeave) // insert the title element into DOM .append(tooltip); }, onMouseEnter: function(x, y) { this.data("tooltip").style({left: x, top: y}).show(); }, onMouseLeave: function() { this.data("tooltip").hide(); }});

    Podemos diseñar el .custom-titleelemento en CSS:

    .custom-title { position: fixed; /* required */ border: 1px solid #faebcc; background: #faf8f0;}

    La parte más interesante ocurre cuando insertas un nuevo elemento con un titleatributo en la página. La información sobre herramientas personalizada funcionará sin ninguna llamada de inicialización.

    Las extensiones en vivo son independientes; por lo tanto, no requieren que usted invoque una función de inicialización para poder trabajar con contenido futuro. Por lo tanto, se pueden combinar con cualquier biblioteca DOM y simplificarán la lógica de su aplicación al separar el código de la interfaz de usuario en muchas partes pequeñas e independientes.

    Por último, pero no menos importante, unas palabras sobre los componentes web . Una sección de la especificación, " Decoradores ", tiene como objetivo resolver un problema similar. Actualmente, utiliza una implementación basada en marcado con una sintaxis especial para adjuntar detectores de eventos a elementos secundarios. Pero todavía es un borrador inicial:

    "Los decoradores, a diferencia de otras partes de los componentes web, aún no tienen una especificación".

    Animaciones nativas

    Gracias a Apple , CSS ahora tiene un buen soporte de animación . En el pasado, las animaciones generalmente se implementaban en JavaScript mediante setIntervaly setTimeout. Era una característica interesante, pero ahora es más bien una mala práctica. Las animaciones nativas siempre serán más fluidas: suelen ser más rápidas, consumen menos energía y se degradan bien si no son compatibles con el navegador.

    En Better-dom, no existe ningún método : animatesolo showy hide. togglePara capturar el estado de un elemento oculto en CSS, la biblioteca utiliza el atributo basado en estándares aria-hidden.

    Para ilustrar cómo funciona, agreguemos un efecto de animación simple a la información sobre herramientas personalizada que presentamos anteriormente:

    .custom-title { position: fixed; /* required */ border: 1px solid #faebcc; background: #faf8f0; /* animation code */ opacity: 1; -webkit-transition: opacity 0.5s; transition: opacity 0.5s;}.custom-title[aria-hidden=true] { opacity: 0;}

    Internamente, show()y hide()establezca el aria-hiddenvalor del atributo en falsey true. Permite que CSS maneje las animaciones y transiciones.

     

    Puedes ver una demostración con más ejemplos de animación que utilizan better-dom .

    Microplantillas integradas

    Las cadenas HTML son molestamente detalladas. Buscando un sustituto, encontré al excelente Emmet . Hoy en día, Emmet es un complemento bastante popular para editores de texto y tiene una sintaxis agradable y compacta. Tome este HTML:

    body.append("ulli class='list-item'/lili class='list-item'/lili class='list-item'/li/ul");

    Y compárelo con la microplantilla equivalente:

    body.append("ulli.list-item*3");

    En Better-dom, cualquier método que acepte HTML también puede usar expresiones Emmet. El analizador de abreviaturas es rápido, por lo que no hay necesidad de preocuparse por una penalización en el rendimiento. También existe una función de precompilación de plantillas que se puede utilizar bajo demanda.

    Apoyo a la Internacionalización

    Desarrollar un widget de interfaz de usuario a menudo requiere localización, lo que no es una tarea fácil. A lo largo de los años, muchos han abordado esto de diferentes maneras. Con Better-dom, creo que cambiar el estado de un selector CSS es como cambiar de idioma .

    Conceptualmente hablando, cambiar de idioma es como cambiar la “representación” del contenido. En CSS2, varios pseudoselectores ayudan a describir dicho modelo: :langy :before. Tome el siguiente código:

    [data-i18n="hello"]:before { content: "Hello Maksim!";}[data-i18n="hello"]:lang(ru):before { content: "Привет Максим!";}

    El truco es simple: el valor de la contentpropiedad cambia según el idioma actual, que está determinado por el langatributo del htmlelemento. Al utilizar atributos de datos como data-i18n, podemos mantener el contenido textual en HTML:

    [data-i18n]:before { content: attr(data-i18n);}[data-i18n="Hello Maksim!"]:lang(ru):before { content: "Привет Максим!";}

    Por supuesto, este tipo de CSS no es exactamente atractivo, por lo que Better-dom tiene dos ayudantes: i18ny DOM.importStrings. El primero se utiliza para actualizar el data-i18natributo con el valor apropiado y el segundo localiza cadenas para un idioma en particular.

    label.i18n("Hello Maksim!");// the label displays "Hello Maksim!"DOM.importStrings("ru", "Hello Maksim!", "Привет Максим!");// now if the page is set to ru language,// the label will display "Привет Максим!"label.set("lang", "ru");// now the label will display "Привет Максим!"// despite the web page's language

    También se pueden utilizar cadenas parametrizadas. Simplemente agregue ${param}variables a una cadena clave:

    label.i18n("Hello ${user}!", {user: "Maksim"});// the label will display "Hello Maksim!"

    Hacer que las API nativas sean más elegantes

    En general, queremos ceñirnos a los estándares. Pero a veces los estándares no son precisamente fáciles de usar. El DOM es un desastre total y, para hacerlo soportable, tenemos que envolverlo en una API conveniente. A pesar de todas las mejoras realizadas por las bibliotecas de código abierto, algunas partes aún podrían mejorarse:

      Todo sobre Apple, Mac e Iphone

    • captador y definidor,
    • manejo de eventos,
    • Soporte de métodos funcionales.

    Captador y fijador

    El DOM nativo tiene el concepto de atributos y propiedades de elementos que podrían comportarse de manera diferente. Supongamos que tenemos el siguiente marcado en una página web:

    a href="/chemerisuk/better-dom" data-test="test"better-dom/a

    Para explicar por qué "el DOM es un desastre total", veamos esto:

    var link = document.getElementById("foo");link.href; // = "https://github.com/chemerisuk/better-dom"link.getAttribute("href"); // = "/chemerisuk/better-dom"link["data-test"]; // = undefinedlink.getAttribute("data-test"); // = "test"link.href = "abc";link.href; // = "https://github.com/abc"link.getAttribute("href"); // = "abc"

    El valor de un atributo es igual a la cadena apropiada en HTML, mientras que la propiedad del elemento con el mismo nombre podría tener algún comportamiento especial, como generar la URL completa en el listado anterior. Estas diferencias pueden resultar confusas.

    En la práctica, es difícil imaginar una situación práctica en la que tal distinción sería útil. Además, el desarrollador siempre debe tener en cuenta qué valor (atributo o propiedad) que se utiliza introduce una complejidad innecesaria.

    En mejores condiciones, las cosas son más claras. Cada elemento tiene sólo captadores y definidores inteligentes.

    var link = DOM.find("#foo");link.get("href"); // = "https://github.com/chemerisuk/better-dom"link.set("href", "abc");link.get("href"); // = "https://github.com/abc"link.get("data-attr"); // = "test"

    En el primer paso, realiza una búsqueda de propiedades y, si está definida, se utiliza para manipulación. De lo contrario, getter y setter trabajan con el atributo apropiado del elemento. Para los valores booleanos (marcados, seleccionados, etc.), puede simplemente usar trueo falsepara actualizar el valor: cambiar dicha propiedad en un elemento activaría la actualización del atributo apropiado (comportamiento nativo).

    Manejo de eventos mejorado

    El manejo de eventos es una gran parte del DOM, sin embargo, descubrí un problema fundamental: tener un objeto de evento en los detectores de elementos obliga a un desarrollador que se preocupa por la capacidad de prueba a burlarse del primer argumento o crear una función adicional que pase solo el evento. propiedades utilizadas en el controlador.

    var button = document.getElementById("foo");button.addEventListener("click", function(e) { handleButtonClick(e.button);}, false);

    Esto es realmente molesto. ¿Qué pasaría si extrayéramos la parte cambiante como argumento? Esto nos permitiría deshacernos de la función extra:

    var button = DOM.find("#foo");button.on("click", handleButtonClick, ["button"]);

    De forma predeterminada, el controlador de eventos pasa la [“target”, “defaultPrevented”]matriz, por lo que no es necesario agregar el último argumento para obtener acceso a estas propiedades:

    button.on("click", function(target, canceled) { // handle button click here});

    También se admite el enlace tardío (recomiendo leer la reseña de Peter Michaux sobre el tema). Es una alternativa más flexible a los controladores de eventos habituales que existen en el estándar del W3C . Podría resultar útil cuando necesite llamadas frecuentes ony offa métodos.

     

    button._handleButtonClick = function() { alert("click!"); };button.on("click", "_handleButtonClick");button.fire("click"); // shows "clicked" messagebutton._handleButtonClick = null;button.fire("click"); // shows nothing

    Por último, pero no menos importante, Better-dom no tiene ninguno de los atajos que existen en las API heredadas y que se comportan de manera inconsistente en todos los navegadores, como click()y . La única forma de llamarlos es utilizar el método, que ejecuta la acción predeterminada cuando ningún oyente ha regresado :focus()submit()firefalse

    link.fire("click"); // clicks on the linklink.on("click", function() { return false; });link.fire("click"); // triggers the handler above but doesn't do a click

    Soporte de métodos funcionales

    ES5 estandarizó un par de métodos útiles para matrices, incluidos map, filtery some. Nos permiten utilizar operaciones de cobranza comunes de forma basada en estándares. Como resultado, hoy tenemos proyectos como Underscore y Lo-Dash , que rellenan estos métodos para navegadores antiguos.

    Cada elemento (o colección) en Better-dom tiene integrados los siguientes métodos:

    • each(que difiere de forEachregresar thisen lugar de undefined)
    • some
    • every
    • map
    • filter
    • reduce[Right]
    var urls, activeLi, linkText; urls = menu.findAll("a").map(function(el) { return el.get("href");});activeLi = menu.children().filter(function(el) { return el.hasClass("active");});linkText = menu.children().reduce(function(memo, el) { return memo || el.hasClass("active") el.find("a").get()}, false);

    Evitar problemas con jQuery

    La mayoría de los siguientes problemas no se pueden solucionar en jQuery sin romper la compatibilidad con versiones anteriores. Por eso la creación de una nueva biblioteca parecía la salida lógica.

    • $la función “mágica”
    • el valor del []operador
    • problemas conreturn false
    • findyfindAll

    La función $ “mágica”

    Todo el mundo ha oído en algún momento que la $función (del dólar) es algo así como magia. Un nombre de un solo carácter no es muy descriptivo, por lo que parece un operador de lenguaje integrado. Es por eso que los desarrolladores sin experiencia lo llaman en línea en todas partes.

    Detrás de escena, el dólar es una función bastante compleja . Ejecutarlo con demasiada frecuencia, especialmente en eventos frecuentes como mousemovey scroll, podría provocar un rendimiento deficiente de la interfaz de usuario.

    A pesar de tantos artículos que recomiendan que los objetos jQuery se almacenen en caché, los desarrolladores continúan insertando la función dólar en línea, porque la sintaxis de la biblioteca los alienta a usar este estilo de codificación.

     

    Otro problema de la función dólar es que nos permite hacer dos cosas completamente diferentes. La gente se ha acostumbrado a esa sintaxis, pero es una mala práctica de diseño de funciones en general:

    $("a"); // = searches all elements that match “a” selector$("a"); // = creates a a element with jQuery wrapper

    En Better-dom, varios métodos cubren las responsabilidades de la función dólar en jQuery: find[All]y DOM.create. find[All]se utiliza para buscar elementos según el selector CSS. DOM.createcrea un nuevo árbol de elementos en la memoria. Sus nombres dejan muy claro de qué son responsables.

    Valor del operador []

    Otra razón del problema de las frecuentes llamadas a funciones en dólares es el operador entre paréntesis. Cuando se crea un nuevo objeto jQuery, todos los nodos asociados se almacenan en propiedades numéricas. Pero tenga en cuenta que el valor de dicha propiedad contiene una instancia de elemento nativo (no un contenedor jQuery):

    var links = $("a");links[0].on("click", function() { ... }); // throws an error$(links[0]).on("click", function() { ... }); // works fine

    Debido a esta característica, cada método funcional en jQuery u otra biblioteca (como Underscore) requiere que el elemento actual esté incluido $()dentro de una función de devolución de llamada. Por lo tanto, los desarrolladores siempre deben tener en cuenta el tipo de objeto con el que están trabajando (un elemento nativo o un contenedor) a pesar de que estén utilizando una biblioteca para trabajar con el DOM.

    En Better-dom, el operador de corchetes devuelve el objeto de una biblioteca, por lo que los desarrolladores pueden olvidarse de los elementos nativos. Sólo hay una forma aceptable de acceder a ellos: mediante un legacymétodo especial.

    var foo = DOM.find("#foo");foo.legacy(function(node) { // use Hammer library to bind a swipe listener Hammer(node).on("swipe", function(e) { // handle swipe gesture here }); });

    En realidad, este método es necesario en casos muy raros, como por ejemplo para ser compatible con una función nativa o con otra biblioteca DOM (como Hammer en el ejemplo anterior).

    Problemas con devolución falsa

    Una cosa que realmente me sorprende es la extraña return falseintercepción en los controladores de eventos de jQuery. Según los estándares del W3C, en la mayoría de los casos debería cancelar el comportamiento predeterminado. En jQuery, return falsetambién detiene la delegación de eventos .

    Esta interceptación crea problemas:

    1. La invocación stopPropagation()por sí sola podría provocar problemas de compatibilidad, porque impide que los oyentes relacionados con alguna otra tarea realicen su trabajo.
    2. La mayoría de los desarrolladores (incluso los más experimentados) no son conscientes de este comportamiento.

    No está claro por qué la comunidad jQuery decidió cruzar estándares. Pero Better-dom no va a repetir el mismo error. Por lo tanto, return falseen un controlador de eventos solo se evita la acción predeterminada del navegador, sin interferir con la propagación de eventos, como todos esperarían.

     

    encontrar y encontrar todo

    La búsqueda de elementos es una de las operaciones más costosas en el navegador. Se podrían utilizar dos métodos nativos para implementarlo: querySelectory querySelectorAll. La diferencia es que el primero deja de buscar en la primera coincidencia.

    Esta característica nos permite disminuir drásticamente el recuento de iteraciones en ciertos casos. ¡En mis pruebas, la velocidad fue hasta 20 veces más rápida! Además, puede esperar que la mejora crezca según el tamaño del árbol de documentos.

    jQuery tiene un findmétodo que se utiliza querySelectorAllpara casos generales. Actualmente, ninguna función utiliza querySelectorpara buscar solo el primer elemento coincidente.

    La biblioteca Better-dom tiene dos métodos separados: findy findAll. Nos permiten utilizar querySelectorla optimización. Para estimar la mejora potencial en el rendimiento, busqué el uso de estos métodos en todo el código fuente de mi último proyecto comercial:

    • find103 coincidencias en 11 archivos
    • findAll14 coincidencias en 4 archivos

    El findmétodo es definitivamente mucho más popular. Significa que querySelectorla optimización tiene sentido en la mayoría de los casos de uso y podría dar un importante impulso al rendimiento.

    Conclusión

    Las extensiones en vivo realmente facilitan mucho la resolución de problemas de front-end. Dividir la interfaz de usuario en muchas partes pequeñas conduce a soluciones más independientes y fáciles de mantener. Pero como hemos demostrado, un marco no se trata sólo de ellos (aunque es el objetivo principal).

    Una cosa que he aprendido en el proceso de desarrollo es que si no te gusta un estándar o tienes una opinión diferente sobre cómo deberían funcionar las cosas, entonces simplemente impleméntalo y demuestra que tu enfoque funciona . ¡También es muy divertido!

    Puede encontrar más información sobre el proyecto Better-dom en GitHub .

    Otras lecturas

    • Presentamos extensiones en vivo para Better-DOM
    • Eventos de entrada del navegador: ¿Podemos hacerlo mejor que el clic?
    • Análisis de las características de la red utilizando JavaScript y DOM
    • Comprender las referencias débiles en JavaScript

    (al, il, ea, mrn)Explora más en

    • Codificación
    • CSS
    • javascript
    • jQuery





    Tal vez te puede interesar:

    1. 40 bibliotecas útiles de JavaScript
    2. Bibliotecas prácticas de JavaScript y complementos de jQuery
    3. Bibliotecas de JavaScript útiles y complementos de jQuery: parte 2
    4. Bibliotecas JavaScript útiles y complementos jQuery

    Escribir una mejor biblioteca de JavaScript para el DOM

    Escribir una mejor biblioteca de JavaScript para el DOM

    Implemente rápidamente. Implementar inteligentemente Clase magistral de CSS moderno avanzado, con Manuel Matuzović Índice

    programar

    es

    https://pseint.es/static/images/programar-escribir-una-mejor-biblioteca-de-javascript-para-el-dom-835-0.jpg

    2024-04-04

     

    Escribir una mejor biblioteca de JavaScript para el DOM
    Escribir una mejor biblioteca de JavaScript para el DOM

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20