AFASSoftware / maquette

Pure and simple virtual DOM library

Home Page:https://maquettejs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Accept array of functions for event handlers

CitrusFruits opened this issue · comments

At my company, we've been using maquette a lot for a new project of ours and one thing that I keep running into and thinking "wow it would be nice if maquette did this" is optionally accepting an array of handlers for any event.

Consider the toy script below

document.addEventListener("DOMContentLoaded", function () {
  const h = maquette.h,
    domNode = document.body,
    projector = maquette.createProjector(),
    store = {},
    calculateWidth = (element) => {
      store.width = `${element.getBoundingClientRect().width}px`;
      projector.scheduleRender();
    },
    storeElement = (element) => {
      store.element = element;
    },
    storeElementAndCalculateWidth = (element) => {
      storeElement(element);
      calculateWidth(element);
    };

  const renderMaquette = () => {
    return h("div", [
      h("div",
        {
          class: "my-div",
          styles: {
            width: `${store.width}`,
          },
        },
        [
          "Div",
        ]
      ),
      h("input",
        {
          afterCreate: storeElementAndCalculateWidth,
        }
      ),
    ]);
  };
  projector.append(domNode, renderMaquette);
});

I would like to remove the need for functions like storeElementAndCalculateWidth. I propose one of two solutions.

  1. accepting arrays into afterCreate, onclick, onkeypress, etc.

i.e.

h("input",
  {
    afterCreate: [storeElement, calculateWidth],
  }
)
  1. Accepting arrays into plural versions of all existing event handlers: afterCreates, onclicks, onkeypresses, etc.
h("input",
  {
    afterCreates: [storeElement, calculateWidth],
  }
)

If you find this to be a good idea, there's a good chance I could make this change and submit a pull request for it.

Thanks for the suggestion. Since changing event handler is no longer prohibited, you can also do:

h("input",
  {
    afterCreate: (el) => { storeElement(el); calculateWidth(el); },
  }
)

I am a bit concerned about the API getting more vague and the size of maquette blowing up. A simple helper function doAll(storeElement, calculateWidth) could also do the job.

A doAll implementation in Typescript would be

let doAll = <X extends Function>(...functions: X[]): X => {
  return <any>((...args: any[]) => {
    functions.forEach(f => f.apply(this, args));
  });
};

Ok, that does seem like an acceptable solution, although I am still curious on how maquette handles the creation of event handlers now in 3.0.

What changed between Maquette 2 and 3, other than the lack of error thrown when handlers are updated?

The algorithm for event handlers in maquette3 is as follows:

  1. Instead of attaching the the event handler that is provided in the VNode to the DOM, we attach a generic event handler.
  2. This generic event handler, when invoked, travels up through the evt.currentTarget's parentElements to the root of the projection, storing the path of DOM Nodes encountered.
  3. The VNode-tree for that projection is then used to follow the path obtained from the previous step to find the right VNode in the tree.
  4. The event handler on that VNode is then invoked.

That's really awesome. Thanks for the help and the explanation, it's interesting stuff.