Breaking

Post Top Ad

Your Ad Spot

martes, 8 de octubre de 2019

Encadenamiento de estilos con un proxy de JavaScript

Una de las delicias de trabajar con Ruby y jQuery es la capacidad de encadenar métodos, lo que le permite invocar convenientemente múltiples métodos en el mismo objetivo. En jQuery, por ejemplo, la mayoría de los métodos devuelven un objeto jQuery, por lo que puede crear una cadena de métodos donde cada nuevo método opera en el objetivo anterior. Esto le permite actualizar algunos estilos, ejecutar una animación y actualizar un atributo, todo sin consultar ese elemento una y otra vez:
$(".menu")
  .css("color", "#fff")
  .data("mode", "light")
  .fadeIn();
Corto y dulce. Si ha actualizado los estilos de un objeto con JavaScript vainilla, es posible que le haya molestado el hecho de que no puede encadenar los cambios de estilo, por lo que debe hacer algo como esto:
let menu = document.querySelector(".menu");
menu.style.color = "#fff";
menu.style.backgroundColor = "#000";
menu.style.opacity = "1";
Hay algunas formas diferentes de hacer que esto sea más conveniente, pero el otro día comencé a pensar si sería posible usar un Proxyobjeto (en el momento de escribir este artículo, el soporte global está en 92.76% ) para permitir el encadenamiento de los cambios de estilo. Resulta que es relativamente fácil. Veremos cómo crear un controlador de proxy ligero que nos permita acortar el código anterior a esto:
style(".menu")
  .color("#fff")
  .backgroundColor("#000")
  .opacity("1");
Usaremos aproximadamente la misma estrategia que jQuery: buscaremos el objeto de estilo de un elemento y lo envolveremos con un Proxy para interceptar (atrapar) todas las getllamadas a ese styleobjeto, tomar la propiedad accedida y actualizar su valor si se pasa un valor y luego se devuelve el Proxycontrolador que envuelve el styleobjeto nuevamente, lo que nos permite construir una cadena infinita de comandos.
Dado que reutilizaremos el método get para que también actúe como setter, retendremos la funcionalidad get devolviendo el valor de una propiedad si no pasa ningún argumento a la función (es decir, obtendrá un valor style(".menu").color()más bien que style(".menu").color). Aquí está la esencia de la técnica:
let styleProxy = {
  get: (object, property) => {
    return (value) => {
      if (value) {
        object[property] = value;
        return new Proxy(object, styleProxy);
      }
      return object[property];
    }
  }
}

let style = (selector) => {
  let element = document.querySelector(selector);

  return new Proxy(element.style, styleProxy);
}
Analicemos, y veamos rápidamente cómo Proxyfunciona.

¡Es una trampa!https://tobiasahlin.com/blog/chaining-styles-with-proxy/#its-a-trap

Los primeros aspectos a entender sobre el uso de un Proxy son handlerstrapsPodemos crear una handlertrapuna serie de operaciones, por ejemplo get()set()apply()En esencia, tendremos la oportunidad de interceptar esas operaciones en el objeto que estamos envolviendo y hacer con ellas lo que queramos: podemos devolver diferentes valores dependiendo de alguna lógica, o simplemente reenviar la operación al objetivo original.
Como ejemplo simple, siempre podemos devolver el mismo valor independientemente de la propiedad a la que intente acceder, incluso si no se ha establecido ninguna propiedad en el objeto original:
let handler = {
  get: () => {
    return "hodor";
  }
}

let person = { name: "Wylis" } 
let proxied = new Proxy(person, handler);

console.log(person.name);  // "Wylis"
console.log(proxied.name); // "hodor"
console.log(proxied.age); //  "hodor"
console.log(proxied.favoriteFood); // "hodor"

Siempre devuelva una funciónhttps://tobiasahlin.com/blog/chaining-styles-with-proxy/#always-return-a-function

Esto nos permite cambiar por completo cómo funciona un objeto. Para habilitar el encadenamiento para el styleobjeto, lo expandiremos getpara que también funcione como setTodavía solo atraparemos get, pero en lugar de devolver el valor de una propiedad cuando se accede, devolveremos una función que devuelve el valor de la propiedad solo si la función se invoca sin ningún argumento. Si se pasa un argumento, lo usaremos para actualizar el valor de esa propiedad.
Comencemos por poner lo básico en su lugar. Creemos una nueva handlerllamada getProxygetcreemos una trampa, donde siempre devolvemos una función. Por lo tanto, si solo registramos una propiedad, obtendremos una función. Pero si invocamos esa función, veremos qué devuelve (en este caso, "prueba"):
let getProxy = {
  get: () => {
    return () => {
      return "test";
    }
  }
}

let proxied = new Proxy({}, getProxy);

console.log( proxied.name );   // Our function: (argument) => { return "test"; }
console.log( proxied.name() ); // The value: "test"

Use la función para obtener y establecer valoreshttps://tobiasahlin.com/blog/chaining-styles-with-proxy/#use-the-function-to-get-and-set-values

Dentro de nuestra nueva función, podemos verificar si se le pasa un argumento cuando se invoca. Si se pasa algo, podemos usar ese argumento para actualizar la propiedad. Si no se pasan argumentos, simplemente podemos devolver el valor de esa propiedad, básicamente manteniendo la getfuncionalidad original y expandiéndola con una setopción.
Creemos un nuevo Proxy, esta vez llamado styleProxyVerificaremos si se le está pasando algo, y obtendremos y configuraremos en consecuencia. Nuestro controlador proxy también está pasando un object(el objeto que estamos envolviendo e interceptando) y un propertyargumento (la propiedad en la que estamos operando), y podemos usar estos dos para operar en el objetivo original.
let styleProxy = {
  get: (object, property) => {
    return (value) => {
      if (value) {
        // "object" is the object that we're wrapping
        // "property" is the property of the object that we're accessing
        // "value" is what we passed to the function
        // Let's use these three to update the style object:
        object[property] = value;
      } else {
        // If no arguments were passed, simply return the
        // value of that property:
        return object[property];
      }
    }
  }
}
Esto permite que el método get de nuestro controlador actúe como setter y getter:
style(".menu").color("#fff"); // Gets a function which updates color to "#fff"
style(".menu").color();       // No arguments passed, just returns "#fff"
Tenga en cuenta que, dado que no estamos creando un trappara la setoperación, aún podemos establecer el valor de una propiedad asignándole un valor directamente:
// Works like expected
style(".menu").color = "#fff";

Devuelva el objeto de estilo envuelto en un proxyhttps://tobiasahlin.com/blog/chaining-styles-with-proxy/#return-the-style-object-wrapped-in-a-proxy

Ahora que tenemos el control de lo que se devuelve después de actualizar una propiedad, simplemente podemos devolver el styleobjeto original envuelto en nuestro Proxycontrolador si se pasa un argumento, completando nuestro método de encadenamiento:
let styleProxy = {
  get: (object, property) => {
    return (value) => {
      if (value) {
        object[property] = value;
        // Return the original target, wrapped in the same Proxy handler
        return new Proxy(object, styleProxy);
      }
      return object[property];
    }
  }
}
Cuando usamos el método de encadenamiento, esto es lo que sucede detrás de escena:
style(".menu")              // Returns the style object in a Proxy
  .color("#fff")            // Updates color and returns a Proxy
  .backgroundColor("#000")  // Updates bgColor and returns a Proxy
  .opacity("1");            // ... and so on so forth
Aquí está la solución completa:
let styleProxy = {
  get: (object, property) => {
    return (value) => {
      if (value) {
        object[property] = value;
        return new Proxy(object, styleProxy);
      }
      return object[property];
    }
  }
}

let style = (selector) => {
  let element = document.querySelector(selector);

  return new Proxy(element.style, styleProxy);
}
No puedo decir con confianza que recomiendo este enfoque, y no lo usaré en este sitio pronto debido a la compatibilidad con el navegador demasiado bajo, pero me parece fascinante lo flexible que es JavaScript y cómo con el Proxy API podemos ir aún más lejos.

No hay comentarios.:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

Post Top Ad

Your Ad Spot

Páginas