# custom-elements/lib (registration) Links: [Home](../index.md) The registration helper for the custom elements of this repository: one function that hands the tag -> module map to the framework's `custom-elements` module, so an element module is downloaded the first time its tag appears in the page. Currently the map has one entry, `` -> [`custom-elements/scroller`](scroller.md). ## Loading the Module ```js Application.require("custom-elements/lib").then(function (registerCustomElements) { registerCustomElements(function (err) { if (err) return console.error(err); // tags are now upgraded as they appear }); }); ``` **Dependency:** the framework module `custom-elements` (`constructors/custom-elements.js`), which observes `document.body` with a `MutationObserver` -- call the helper after the body exists. ## API ### `registerCustomElements(cb)` `module.exports` is the function itself, so the `require` resolves immediately; calling the function does the registration. | Parameter | Type | Description | |---|---|---| | `cb` | `function (err)` | **Mandatory.** `cb()` right after `lazyLoadModules()` has been called; `cb(err)` when requiring the framework `custom-elements` module failed (the error is also logged) | Returns `undefined`. The map it registers: | Tag | Module | Page | |---|---|---| | `sgapps-scroller` | `custom-elements/scroller` | [sgapps-scroller](scroller.md) | `lazyLoadModules` requires the module right away for a tag already present in the document, and otherwise waits until the observer sees the first element with that tag name. The callback reports the registration only: a failure of the element module itself (404, missing dependency) is logged by the framework and never reaches `cb`. To know when the element is ready, require it yourself -- `App.require('custom-elements/scroller')` resolves with `true` after the tag is registered and its stylesheet linked. ## How the framework's custom elements work The elements do not use `customElements.define()`. The framework module keeps a registry of callback objects per tag name -- `registerMethods(tag, { __onInit, __onAttrChange, __onContentChange, __onRemove, ...methods })` -- and the observer calls them with `this` set to the matching element whenever such an element is added, changed or removed (`__onInit` also runs synchronously for the elements already in the page at registration time). Custom methods are reachable as `element.methods.()`. A hyphenated tag such as `` is an ordinary `HTMLElement` until its module upgrades it, so the markup can be parsed before the module loads. Options are read from `element.attrdata` (`extensions/prototype`): `attr-foo-bar="x"` gives `attrdata.fooBar = "x"` (string), `attrb-foo-bar="true"` a `JSON.parse`d value (boolean / number / object), `attrf-foo-bar="expr"` an evaluated expression. The bag is built on the first access and cached. ## Adding your own element ```js // my-elements/badge.js Application.require(["customElements :: custom-elements", "uriLoad :: uri-load"]).then(function (lib) { lib.customElements.registerMethods('my-badge', { __onInit: function () { // this = the element this.textContent = this.attrdata.label || this.textContent; this.attrdata.count = 0; }, __onAttrChange: function () { /* attributes changed */ }, __onRemove: function () { /* clean up */ }, increment: function () { // element.methods.increment() this.attrdata.count += 1; this.setAttribute('data-count', this.attrdata.count); } }); lib.uriLoad.link(module.resourceUrl('badge/style.css'), function () { module.exports = true; }); }); ``` Register it next to the scroller by extending the map in `custom-elements/lib.js` (`"my-badge": "my-elements/badge"`) and adding the module name to `lib.js` at the repository root, or call `customElements.lazyLoadModules({ 'my-badge': 'my-elements/badge' })` from your own code. ## Notes - Register the tags once per page; a second call re-registers the same map harmlessly. - Live demo: the [sgapps-scroller](playground.html#module=scroller ':ignore :target=_blank') playground page boots through `custom-elements/lib`.