[dom] MutationObserver with querySelector for elements (#77)

MutationObserver is a great tool for writing small libraries that operate on specific types of elements. 
Examples:

* Libraries that extend the behavior of particular element
** Date/Time pickers on `<input type="datetime">`
** Additional API on some types of `<link>` elements
* Libraries that modify custom elements (from outside extension of web-components)
* Libraries that modify elements with certain attributes:
** Libraries that aid with specific `aria-*` values
** Libraries that handle localization of `data-l10n-*` elements


Currently, MutationObserver API is designed to aid code that wants to operate on all subtree operations of a given root.
But in all those cases the basic premise is that they want to handle specific set of elements. Their additions/removals and possibly modifications.

The result is that in all those cases that I encountered in my work there's a massive amount of noise generated because the library is fishing for a particular element type, while it has to retrieve all node additions/removals and filter them out manually.

In many cases, the ratio of nodes inserted (dynamically or by the parser) to elements the library wants to operate on is 100/1.

My understanding is that the platform is in much better position to optimize filtering the elements for MutationObserver than anything that the MutationObserver callback can do.
Additionally, even if the code logic would be the same, the sole fact that the `mutations` argument has to be constructed and then the callback for them has to be called, for many mutations that are not relevant to the MutationObserver is a waste of CPU time and memory/GC allocation.

My initial proposal would look something like this:

```
const observerConfig = {
  attributes: true,
  attributeFilter: ['data-l10n-id', 'data-l10n-args'],
  childNodes: true,
  querySelector: '*[data-l10n-id], *[data-l10n-args]'
}; 

var mo = new MutationObserver(onMutations);
mo.observe(this.doc, observerConfig);

function onMutations(mutations) {
  const targets = new Set();
  const removedTargets = new Set();

  for (let mutation of mutations) {
    switch (mutation.type) {
      case 'attributes':
        targets.add(mutation.target);
        break;
      case 'childList':
        mutation.addedNodes.forEach(node => targets.add(node));
        mutation.removedNodes.forEach(node => removedTargets.add(node));
        break;
    }
  }

  translateElements(targets);
  cleanupAfterElements(removedTargets);
} 
```

---
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/77

Received on Friday, 18 September 2015 22:02:44 UTC