Skip to content

Experimental DOM feature suggestion for applying bulk property updates to elements

License

Notifications You must be signed in to change notification settings

ComponentKitchen/bulk-properties

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bulk property updates for HTML elements

This repository considers the possibility of introducing new DOM API methods to efficiently set multiple HTML element properties at once, on either a single element or a collection of elements identified with ids.

Example:

const div = document.createElement('div');
div.applyProperties({
  attributes: {
    role: 'main'
  },
  style: {
    color: 'red'
  },
  textContent: 'Hello, world.'
});

The above would be exactly equivalent to:

const div = document.createElement('div');
div.setAttribute('role', 'main');
div.style.color = 'red';
div.textContent = 'Hello, world.';

In either case, the result would be the same:

<div role="main" style="color: red;">Hello, world.<div>

Live demo (Source)

Goals

  • Generally facilitate population and manipulation of DOM from JavaScript, e.g., in Functional-Reactive Programming (FRP) architectures. Rather that introducing new capabilities, this simply formalizes very common JavaScript patterns for creating and updating DOM elements.
  • Support fast updates by allowing the browser to update multiple properties and elements in a single DOM call.
  • Allow web components to efficiently reflect state changes in their shadow trees.

applyProperties method

The applyProperties method would be added to Element.prototype. This method applies a dictionary of properties to the indicated element.

Modifying designated collection properties

The Element prototype exposes several properties that expose a collection that can be modified but not set directly: attributes, childNodes, classList, and (for HTMLElement and SVGElement) style. Although these properties cannot be directly set (you can't write element.attributes = newAttributes), we can define simple and useful write semantics for updating these properties in the context of the applyProperties method.

In each case, we define the write semantics in terms of existing DOM write methods:

  • attributes property: Sets multiple attributes at once. This takes a subdictionary in which each name: value is equivalent to calling setAttribute(key, value). Passing a nullish value acts like removeAttribute(key). Known Boolean attributes (e.g., disabled) are slightly different: a truthy value has the effects of setAttribute(key, ''), and a falsy value acts like removeAttribute(key).

  • childNodes property: Sets an element's childNodes. This takes a NodeList or array of Node elements. This is equivalent to calling removeChild() on any nodes not in the supplied value, then calling appendChild() for each node in the supplied value.

  • classList property: Sets/clears multiple classes at once. This takes a subdictionary in which each name: value is equivalent to calling classList.toggle(name, value).

  • style property: Sets multiple style values at once. This takes a subdictionary in which each name: value is equivalent to calling style[name] = value. Attempting to update style on something other than a HTMLElement or SVGElement throws an exception.

Note that applyProperties is updating the indicated properties, leaving in place any other existing element attributes, classes, or styles not specifically referenced in the dictionary:

const div = document.createElement('div');
div.classList.add('foo bar');
div.applyProperties({
  classList: {
    bar: false, // Removes bar
    baz: true   // Adds baz
  }
});
div.classList.value // "foo baz"

The ability to update childNodes facilitates construction of DOM through code that, for example, maps an array of model objects to an array of DOM elements that should be added to the DOM.

const objects = [...];
const elements = objects.map(object => 
  document.createElement('div', { properties })
);
document.body.applyProperties({
  childNodes: elements
});

Live demo (Source)

Modifying other properties

All other property dictionary keys result in invoking the property with the corresponding name. E.g.,

element.applyProperties({ foo: 'bar' });

is equivalent to

element.foo = 'bar';

This can be used to set both native HTML element properties as well as custom element properties.

applyPropertiesById method

A related method, applyPropertiesById, allows the application of properties to a set of elements identified by id. This method would be exposed on Document and DocumentFragment.

<body>
  <div id="foo">
    <div id="bar"></div>
  </div>
</body>
document.applyPropertiesById({
  foo: {
    style: {
      color: 'red'
    }
  },
  bar: {
    textContent: 'Hello, world.'
  }
});

Live demo (Source)

For each key: value in the supplied dictionary, applyPropertiesById takes the key as an id, and finds the corresponding element in the relevant tree via getElementById(key). If the element is found, it passes the value as a property dictionary to applyProperties(element, value).

The above code is exactly equivalent to:

const foo = document.getElementById('foo');
foo.style.color = 'red';
const bar = document.getElementById('bar');
bar.textContent = 'Hello, world.';

In both cases, the result is:

<body>
  <div id="foo" style="color: red;">
    <div id="bar">Hello, world.</div>
  </div>
</body>

The use of applyPropertiesById is particularly useful in web components that want to reflect component state in their shadow tree. E.g., when component state changes, it might invoke

const changes = {
  element1: { /* changes for element1 */ },
  element2: { /* changes for element2 */ },
  ...
}
this.shadowRoot.applyPropertiesById(changes);

Live demo (Source)

document.createElement parameter

It would be useful to accept the same properties dictionary as an options parameter to createElement/createElementNS. The example at the top of this document could be written more concisely as:

const element = document.createElement('div', {
  attributes: {
    role: 'main'
  },
  style: {
    color: 'red'
  },
  textContent: 'Hello, world.'
});

Note: createElement/createElementNS currently accept an additional argument with options, which at the moment is just the standard (but not universally supported) is option. There are likely several ways the existing options parameter could be reconciled with the suggestion above.

About

Experimental DOM feature suggestion for applying bulk property updates to elements

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published