Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development
Code

Construye tu Primera Biblioteca de JavaScript

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Betania Carolina León Alfonzo (you can also view the original English article)

¿Alguna vez te maravillaste con la magia de Mootools? ¿Alguna vez te has preguntado cómo lo hace Dojo? ¿Alguna vez has sido curioso sobre la gimnasia de jQuery? En este tutorial, vamos a escabullirnos entre bastidores e intentaremos construir una versión súper simple de tu biblioteca favorita.

Usamos bibliotecas de JavaScript casi todos los días. Cuando recién estás empezando, tener algo como jQuery es fantástico, principalmente por el DOM. En primer lugar, el DOM puede ser bastante complicado para un principiante; es una excusa bastante pobre para una API. En segundo lugar, ni siquiera es consistente en todos los navegadores.

Estamos envolviendo los elementos en un objeto porque queremos poder crear métodos para el objeto.

En este tutorial, vamos a hacer una prueba (decididamente superficial) para construir una de estas bibliotecas desde cero. Sí, será divertido, pero antes de que te emociones, permíteme aclarar algunos puntos:

  • Esta no será una biblioteca con todas las funciones. Oh, tenemos un conjunto sólido de métodos para escribir, pero no es jQuery. Haremos lo suficiente para darle una buena sensación del tipo de problemas que encontrará al crear bibliotecas.
  • No buscamos la compatibilidad completa del navegador en todos los ámbitos aquí. Lo que escribimos hoy debería funcionar en Internet Explorer 8+, Firefox 5+, Opera 10+, Chrome y Safari.
  • No vamos a cubrir todos los usos posibles de nuestra biblioteca. Por ejemplo, nuestros métodos de append y prepend  sólo funcionarán si se les pasa una instancia de nuestra biblioteca; no funcionarán con nodos DOM ni listas de nodos.

Una cosa más: no escribiremos pruebas para esta biblioteca, ni siquiera lo hice cuando desarrollé esto por primera vez. Puede obtener la biblioteca y las pruebas en Github .


Paso 1: Creando el Boilerplate (o placa) de la biblioteca

Comenzaremos con algún "wrapper", que contendrá toda nuestra biblioteca. Es la típica immediately invoked function expression (IIFE). o en español: funciones que son invocadas inmediatamente.

Como puedes ver, estamos llamando a nuestra biblioteca Dome, porque es principalmente una biblioteca DOM. Sí, es patético.

Tenemos un par de cosas sucediendo aquí. Primero, tenemos una función; que eventualmente será una función constructora para las instancias de nuestra biblioteca; esos objetos envolverán nuestros elementos seleccionados o creados.

Entonces, tenemos nuestro objeto dome , que es nuestro objeto de la actual biblioteca; como puedes ver, se devuelve al final allí. Este tiene la función get (obtener), que utilizaremos para seleccionar elementos de la página. Por lo tanto, vamos a ocuparnos de esto ahora.


Paso 2: Obtención de Elementos

La función dome.get tomará un parámetro, pero podría ser una serie de cosas. Si es una cadena, asumiremos que es un selector de CSS; pero también podemos tomar un solo nodo DOM o una lista de nodos.

Estamos usando document.querySelectorAll para simplificar la búsqueda de elementos: por supuesto, esto limita el soporte de nuestro navegador, pero para este caso, está bien. Si el selector no es una cadena, verificaremos una propiedad de longitud (lenght). Si existe, sabremos que tenemos una lista de nodos (NodeList) ; de lo contrario, tenemos un solo elemento y lo pondremos en una matriz. Eso es porque necesitamos una matriz para llamar a nuestro código Dome a la parte inferior; como puedes ver, estamos devolviendo un nuevo objeto Dome . Así que regresemos a la vacía función Dome  y completémosla.


Paso 3: Crear Instancias de Dome

Aquí está la función Dome:

Realmente recomiendo que busques en algunas de tus bibliotecas favoritas.

Esto es realmente simple: solo iteramos sobre los elementos que seleccionamos y los pegamos en el nuevo objeto con índices numéricos. Luego, agregamos una propiedad de longitud (length).

Pero ¿cuál es el punto aquí? ¿Por qué no solo devolver los elementos? Estamos envolviendo o aplicando "wrapping" a los elementos en un objeto porque queremos poder crear métodos para el objeto; éstos son los métodos que nos permitirán interactuar con esos elementos. Esta es en realidad una versión resumida de la forma en que jQuery funciona.

Entonces, ahora que tenemos nuestro objeto Dome devuelto, agreguemos algunos métodos a su prototipo. Voy a poner esos métodos justo debajo de la función Dome.


Paso 4: Agregar Algunas Utilidades

Las primeras funciones que vamos a escribir son simples funciones de utilidad. Dado que nuestros objetos Dome podrían envolver más de un elemento DOM, tendremos que recorrer cada elemento en casi todos los métodos; por lo tanto, estas utilidades serán necesarias.

Empecemos con una función de mapa (map):

Por supuesto, la función de map toma un solo parámetro, una función que llamará de vuelta (callback). Recorreremos los elementos de la matriz y recopilaremos lo que se devuelva de la función "callback" en la matriz de resultados (results). Observe cómo estamos llamando a esa función de devolución de llamada (callback):

Al hacerlo de esta manera, se llamará a la función en el contexto de nuestra instancia de Dome, y recibirá dos parámetros: el elemento actual y el número de índice.

También queremos la función forEach . La cual es realmente muy simple:

Como la única diferencia entre map y forEach es que map debe devolver algo, podemos pasar nuestra devolución de llamada o callback a this.map e ignorar la matriz devuelta; en cambio, devolveremos esto (return this)  para hacer nuestra biblioteca bastante conectada. Vamos a utilizar mucho forEach. Entonces, puedes ver que cuando devolvemos nuestro this.forEach se hace desde una función, en realidad estamos "returning this" o devolviendo esto. Por ejemplo, estos métodos en realidad devuelven lo mismo:

Una cosa más: mapOne. Es fácil ver qué hace esta función, pero la pregunta real es: ¿por qué la necesitamos? Esto requiere un poco de lo que podríamos llamar "filosofía de la biblioteca".

Un Breve Desvío "Filosófico"

En primer lugar, el DOM puede ser bastante complicado para un principiante; es una excusa bastante pobre para una API.

Si la construcción de una biblioteca fuera solo para escribir el código, no sería un trabajo demasiado difícil. Pero mientras trabajaba en este proyecto, encontré que la parte más difícil era decidir cómo deberían funcionar ciertos métodos.

Pronto, vamos a construir un método de texto (text) que devuelve el texto de los elementos seleccionados. Si nuestro objeto Dome envuelve varios nodos DOM (dome.get ("li") , por ejemplo), ¿qué debería hacer esto? Si haces algo similar en jQuery ($ ("li"). Text ()), obtendrás una sola cadena con el texto de todos los elementos concatenados juntos. ¿Esto es útil? No lo creo, pero no estoy seguro de cuál sería el mejor valor de retorno.

Para este proyecto, devolveré el texto de varios elementos como una matriz, a menos que solo haya un elemento en la matriz; entonces solo devolveremos la cadena de texto, no una matriz con un solo elemento. Creo que lo más frecuente es que obtenga el texto de un solo elemento, así que optimizamos para ese caso. Sin embargo, si obtiene el texto de varios elementos, le devolveremos algo con lo que puede trabajar.

Volvamos a la Codificación

Entonces, el método mapOne simplemente ejecutará el mapa (map) y luego devolverá la matriz o el único elemento que se encontraba en la matriz. Si aún no estás seguro de qué es útil, quédate: ¡ya verás!


Paso 5: Trabajar con Texto y HTML

A continuación, vamos a añadir el método de texto (text). Al igual que jQuery, podemos pasarle una cadena y establecer el texto del elemento, o no usar parámetros para recuperar el texto.

Como es de esperar, necesitamos verificar un valor en el texto (text) para ver si estamos estableciendo u obteniendo. Ten en cuenta que el método if (text) no funcionaría, cuando una cadena vacía es un valor falso.

Si estamos configurando, haremos un forEach sobre los elementos y estableceremos su propiedad innerText en el texto (text). Si estamos recibiendo, devolveremos la propiedad innerText de los elementos . Tenga en cuenta nuestro uso del método mapOne: si estamos trabajando con varios elementos, esto devolverá una matriz; de lo contrario, será sólo la cadena.

El método html hará lo mismo que el texto (text), excepto que usará la propiedad innerHTML , en lugar de innerText .

Como he dicho: casi idéntico.


Paso 6: Clases de Hacking (Hackeo)

A continuación, queremos poder agregar y eliminar clases; así que vamos a escribir los métodos addClass y removeClass.

Nuestro método addClass tomará una cadena o una matriz de nombres de clase. Para que esto funcione, necesitamos verificar el tipo de ese parámetro. Si es una matriz, lo recorreremos y crearemos una cadena de nombres de clase. De lo contrario, solo agregaremos un espacio al frente del nombre de la clase, para que no se mezcle con las clases existentes en el elemento. Luego, simplemente tenemos un loop de los elementos y agregamos las nuevas clases a la propiedad className.

Bastante sencillo, ¿eh?

Ahora, ¿qué pasa con la eliminación de clases? Para mantenerlo simple, solo permitiremos eliminar una clase a la vez.

En cada elemento, dividiremos el.className en una matriz. Luego, usamos un loop por los momentos para cortar la clase ofensiva hasta que cs.indexOf (clazz) devuelva -1. Hacemos esto para cubrir el caso extremo donde las mismas clases se han agregado a un elemento más de una vez: necesitamos asegurarnos de que realmente se haya ido. Una vez que estemos seguros de que hemos eliminado todas las instancias de la clase, unimos la matriz con espacios y la configuramos en el.className .


Paso 7: Arreglando un Error de IE

El peor navegador que estamos manejando es IE8. En nuestra pequeña biblioteca, solo hay un error de IE con el que tenemos que lidiar; afortunadamente, es bastante simple. IE8 no es compatible con el método de Array: el indexOf; el cual usamos en removeClass, así que vamos a rellenarlo:

Es bastante simple, y no es una implementación completa (no es compatible con el segundo parámetro), pero funcionará para nuestros propósitos.


Paso 8: Ajuste de Atributos

Ahora, queremos una función attr. Esto será fácil, porque es prácticamente idéntico a nuestros métodos de texto (text) o html. Al igual que esos métodos, podremos obtener y establecer atributos: tomaremos un nombre y valor de atributo para establecer, y solo un nombre de atributo para obtener.

Si val tiene un valor, recorreremos los elementos y estableceremos el atributo seleccionado con ese valor, utilizando el método setAttribute del elemento. De lo contrario, usaremos mapOne para devolver ese atributo a través del método getAttribute.


Paso 9: Creando Elementos

Deberíamos poder crear nuevos elementos, como cualquier buena biblioteca. Por supuesto, esto no sería bueno como método en una instancia de Dome, así que vamos a ponerlo en nuestro objeto de domo (dome).

Como puede ver, tomaremos dos parámetros: el nombre del elemento y un objeto de atributos. La mayoría de los atributos se aplican a través de nuestro método attr, pero dos recibirán un tratamiento especial. Usaremos el método addClass para la propiedad className y el método de texto (text) para la propiedad text. Por supuesto, primero tendremos que crear el elemento y el objeto Dome. Aquí está todo eso en acción:

Como puede ver, creamos el elemento y lo enviamos directamente a un nuevo objeto Dome. Entonces, nos ocupamos de los atributos. Observe que tenemos que eliminar los atributos className y text después de trabajar con ellos. Esto evita que se apliquen como atributos cuando pasamos por el resto de las teclas en los atributos (attrs). Por supuesto, terminamos devolviendo el nuevo objeto Dome.

Pero ahora que estamos creando nuevos elementos, querremos insertarlos en el DOM, ¿verdad?


Paso 10: Anexar y Preponer Elementos

El siguiente, vamos a escribir los métodos append y prepend, ahora, estos son en realidad un poco complejos para diseñar funciones, principalmente debido a los múltiples casos de uso. Esto es lo que queremos poder hacer:

El peor navegador que estamos manejando es IE8.

Los casos de uso son como estos: es posible que desee adjuntar (append) o anteponer (prepend)

  • un nuevo elemento para uno o más elementos existentes.
  • múltiples nuevos elementos a uno o más elementos existentes.
  • un elemento existente a uno o más elementos existentes.
  • múltiples elementos existentes a uno o más elementos existentes.

Nota: estoy usando "nuevo" para significar elementos que aún no están en el DOM; Los elementos existentes ya están en el DOM.

Vamos a ver ahora:

Esperamos que el parámetro els sea un domo (Dome) objeto. Una biblioteca DOM completa aceptaría esto como un nodo o lista de nodos, pero no lo haremos. Tenemos que recorrer cada uno de nuestros elementos, y luego dentro de eso, hacemos un loop sobre cada uno de los elementos que queremos agregar.

Si estamos agregando el parámetro els a más de un elemento, necesitamos clonarlos. Sin embargo, no queremos clonar los nodos la primera vez que se agregan, solo las veces posteriores. Así que haremos esto:

Esa i viene del exterior del loop forEach: el cual es el índice del elemento primario actual. Si no estamos agregando al primer elemento padre, clonaremos el nodo. De esta manera, el nodo real entrará en el primer nodo primario y todos los demás padres recibirán una copia. Esto funciona bien, porque el objeto Dome que se pasó como argumento solo tendrá los nodos originales (sin clonar). Por lo tanto, si solo estamos agregando un solo elemento a un solo elemento, todos los nodos involucrados serán parte de sus respectivos objetos Dome .

Finalmente, añadiremos el elemento:

Entonces, en conjunto, esto es lo que tenemos:

El método prepend

Queremos cubrir los mismos casos para el método prepend, por lo que el método es bastante similar:

Lo diferente cuando se antepone es que si antepedimos o hacemos "prepend" secuencialmente una lista de elementos a otro elemento, terminarán en orden inverso. Ya que no podemos aplicar forEach hacia atrás, estoy pasando por un loop hacia atrás con un loop de for. Nuevamente, clonaremos el nodo si este no es el primer padre al que estamos agregando.


Paso 11: Eliminando Nodos

Para nuestro último método de manipulación de nodos, queremos poder eliminar los nodos del DOM. Fácil, realmente:

Simplemente debemos repetir por los nodos y aplicar el método removeChild sobre cada elemento de parentNode. Lo hermoso aquí (todo gracias al DOM) es que este objeto Dome todavía funcionará bien; podemos usar cualquier método que queramos, incluyendo agregarlo o añadirlo de nuevo al DOM. Bien, ¿eh?


Paso 12: Trabajar con Eventos

Por último, pero ciertamente no menos importante, vamos a escribir algunas funciones para los controladores de eventos.

Como probablemente sepas, IE8 usa los eventos antiguos de IE, así que tendremos que comprobarlo. Además, incluiremos los eventos DOM 0, solo porque podemos.

Echa un vistazo al método, y luego lo discutiremos:

Aquí, tenemos un IIFE, y dentro de él estamos haciendo una verificación de características. Si existe document.addEventListener, lo usaremos; de lo contrario, buscaremos document.attachEvent o recurriremos a los eventos DOM 0. Observe cómo estamos devolviendo la función final del IIFE: eso es lo que terminará siendo asignado a Dome.prototype.on. Al realizar la detección de características, es realmente útil poder asignar la función apropiada de esta manera, en lugar de verificar las características cada vez que se ejecuta la función.

La función off, que desengancha los controladores de eventos, es prácticamente idéntica:


¡Eso es!

Espero que pruebes nuestra pequeña biblioteca, y quizás incluso la extiendas un poco. Como mencioné anteriormente, lo tengo en Github , junto con el conjunto de pruebas Jasmine para el código que hemos escrito arriba. Siéntase libre de bifurcarlo, jugar y enviar una solicitud de extracción.

Aclaro otra vez: el punto de este tutorial no es para sugerir que siempre debes escribir tus propias bibliotecas.

Hay equipos dedicados de personas que trabajan juntas para hacer que las bibliotecas grandes y establecidas sean lo mejor posible. El punto aquí era dar un pequeño vistazo a lo que podría ocurrir dentro de una biblioteca; espero que hayas recogido algunos consejos aquí.

Realmente recomiendo que busques en algunas de tus bibliotecas favoritas. Descubrirás que no son tan crípticos como podría haber pensado, y probablemente aprenderás mucho. Aquí hay algunos buenos lugares para comenzar:

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.