A collection of Custom Elements v1 helpers
npm install custom-elements-helpersA collection of Custom Elements helpers for the v1 spec.
This documentation is in alphabetical order. If you want a basic understanding of how things work, skip the Attributes & Elements chapters. It's most important to know how BaseController and defineCustomElement works. Everything else is optional.
Attributes are packages of properties & methods that you can mix in on your custom elements controllers.
Important: Usage on native HTML elements, e.g. or , is not possible due to browser support restrictions.
The media attribute adds media query support to your custom element.
#### Properties
- media - string - Value of the media attribute
- matchesMedia - boolean - True when the current viewport matches the media query
#### Methods
- whenMediaMatches() - Returns a Promise that resolves once the media query first is matched
- whenMediaUnmatches() - Returns a Promise that resolves once the media query first is unmatched
- watchMedia(match, unmatch) - Takes two callbacks that are called when the media query matches and unmatches, respectively
#### Example usage
The following example will mixin the media attribute.
The init().render().bind() cycle from the base controller will only start once the viewport matches the (min-width: 768px) media query (because of the resolve() override).
##### Javascript
``js
import { defineCustomElement, BaseController, AttrMedia as media } from 'custom-elements-helpers';
defineCustomElement('foo-bar', {
attributes: [ media ],
controller: class extends BaseController {
resolve() {
return Promise.all([
super.resolve(),
this.whenMediaMatches()
]);
}
}
});
`
##### HTML
`html`
The touch-hover attribute adds touch support for hover interactions.is-touch
It adds and is-hover classes to your custom element so that you can style your animations accordingly.
#### Properties
- touchHover - Returns auto when touch-hover is set. Else, returns false.
#### Methods
- enableTouchHover() - Call this method inside the bind method of your custom element
This binds some methods to touchstart & click, so that it works like this:
On non-touch devices, a hover is a hover and a click is a click.
On touch devices, a first tap adds the is-touch class to the element. From this moment on you know that the current device is a touch device. The first tap also adds the is-hover class. A second tap removed the is-hover class.
It also makes sure the touch device's native :hover handling gets blocked properly so you can handle it like you wish.
#### Example usage
`js
import { defineCustomElement, BaseController, AttrTouchHover as touchHover } from 'custom-elements-helpers';
defineCustomElement('foo-bar', {
attributes: [ touchHover ],
controller: class extends BaseController {
bind() {
this.enableTouchHover();
return this;
}
}
});
`
`html
Foo!
`
`css
.foo-bar {
display: inline-block;
position: relative;
}
.foo-bar__show-on-hover {
display: none;
position: absolute;
left: 0;
top: 120%;
width: 100%;
}
.foo-bar:not(.is-touch):hover .foo-bar__show-on-hover,
.foo-bar.is-hover .foo-bar__show-on-hover {
display: block;
}
`
Currently, there is only one controller, conveniently named BaseController.
It's important to extend from this controller if you create an element using defineCustomElement from the utils.
A BaseController instance is a standardized wrapper for your custom element logic. It lays out a consistent blueprint across developers & projects that streamlines the lifecycle of your controller.
#### Lifecycle: Birth
With Custom Elements, your controller gets triggered by your browser automatically when the element you defined gets attached to the DOM. This is what happens next:
##### Step 1: Resolve
The resolve method should return a Promise. Promise
It allows you to wait for something, preload something, … If you want your controller to kick in immediately, return a resolved , by using return Promise.resolve(true).
The default behavior is to wait until document.readyState === 'complete'.
Once the promise has resolved, your custom element will have the is-resolved class attached. You can use this to style your element differently when it's in a loading state.
##### Step 2: Init
Once your custom element instance is resolved, the init method is called. You can do your initial setup here.
##### Step 3: Render
After your initial setup, the render method is called. You can assume a DOM-ready and configured environment here. this.el has a reference to the custom element DOM node so go wild (if you have to).
##### Step 4: Bind
With everything rendered, now's the time to attach your event listeners, using this.on and this.once. Read more below.
#### Working with events
There are four methods on the BaseController that will help you tremendously when working with events and custom elements. When you use these methods, you make sure that event listeners that you've added are removed when the custom element is no longer in the DOM etc.
You use on(name, handler, target = null, options = false) when you want to listen for an event. target falls back to this.el. You can also pass a selector in the name (e.g. click .button). When you do this, your handler gets a second argument pointing to the target.
Some examples:
`js
this.on('mouseenter', (e) => {
console.log('Mouse entered the custom element');
});
this.on('click .button', (e, target) => {
console.log('Click on', button.textContent');
});
this.on('mousemove', (e) => {
console.log('Mouse move somewhere on body');
}, document.body);
`
The once() method has the same footprint but will remove itself after one event.
These event listeners can also be removed. Use off(name, target = null) to remove an event listener registered through on. target falls back to this.el.
Keep in mind that all targets for the same event should be removed separately. If you have a click handler on both this.el and document.body, you would have to call:
`jsthis.el
this.off('click'); // Falls back to `
this.off('click', document.body);
A custom element can also emit events to communicate to other parts of your website.
Using emit(name, data = {}, options = {}) you can easily pass around data.
The data object will be passed on to your handler as e.detail. The options object can have bubbles and cancelable as specified in the spec.
An example:
`jsfoo-bar
// A setter on your element
set current(to) {
this._current = to;
this.emit('foo-bar:current', { current: this._current });
}
// In another custom element its bind() methodCurrent changed to ${e.detail.current}
this.on('foo-bar:current', (e) => {
console.log();`
}, document.body);
#### Lifecycle: Death
When an element gets detached from the DOM, the BaseController goes through some housekeeping methods.
##### Step 1: Destroy
Remove the is-resolved class from the element.
##### Step 2: Unbind
Remove all event listeners that you registered through on or once.
Wrap AJAX form around a working