Registration and lifecycle
Table of contents
Register a custom element
To register a custom element, use the Polymer
function, and pass in the
prototype for the new element. The prototype must have an is
property that
specifies the HTML tag name for your custom element.
By specification, the custom element’s name must contain a dash (-).
Example:
// register an element
MyElement = Polymer({
is: 'my-element',
// See below for lifecycle callbacks
created: function() {
this.textContent = 'My element!';
}
});
// create an instance with createElement:
var el1 = document.createElement('my-element');
// ... or with the constructor:
var el2 = new MyElement();
The Polymer
function registers the element with the browser and returns a
constructor that can be used to create new instances of your element via code.
The Polymer
function sets up the prototype chain for your custom element,
chaining it to the Polymer Base
prototype (which provides
Polymer value-added features), so you cannot set up your own
prototype chain. However, you can use behaviors to
share code between elements.
Define a custom constructor
The Polymer
method returns a basic constructor that can be used to
instantiate the custom element. If you want to
pass arguments to the constructor to configure the new element, you can
specify a custom factoryImpl
function on the prototype.
The constructor returned from Polymer
creates an instance using
document.createElement
, then invokes the user-supplied factoryImpl
function
with this
bound to the element instance. Any arguments passed to the actual
constructor are passed on to the factoryImpl
function.
Example:
MyElement = Polymer({
is: 'my-element',
factoryImpl: function(foo, bar) {
this.foo = foo;
this.configureWithBar(bar);
},
configureWithBar: function(bar) {
...
}
});
var el = new MyElement(42, 'octopus');
Two notes about the custom constructor:
-
The
factoryImpl
method is only invoked when you create an element using the constructor. ThefactoryImpl
method is not called if the element is created from markup by the HTML parser, or if the element is created usingdocument.createElement
. -
The
factoryImpl
method is called after the element is initialized (local DOM created, default values set, and so on). See Ready callback and element initialization for more information.
Extend native HTML elements
Polymer currently only supports extending native HTML elements (for example,
input
, or button
, as opposed to extending other custom elements, which will
be supported in a future release). These native element extensions are called
type extension custom elements.
Note: When using native shadow DOM, extension of native elements can have unexpected behavior and is sometimes not permitted. Test your element with native shadow DOM enabled to catch any problems during development. For information on enabling native shadow DOM, see Global Polymer settings.
To extend a native HTML element, set the extends
property on your prototype to
the tag name of the element to extend.
Example:
MyInput = Polymer({
is: 'my-input',
extends: 'input',
created: function() {
this.style.border = '1px solid red';
}
});
var el1 = new MyInput();
console.log(el1 instanceof HTMLInputElement); // true
var el2 = document.createElement('input', 'my-input');
console.log(el2 instanceof HTMLInputElement); // true
To use a type-extension element in markup, use the native tag and add an
is
attribute that specifies the extension type name:
<input is="my-input">
Define an element in the main HTML document
Note: You should only define elements from the main document when experimenting. In production, elements should always be defined in separate files and imported into your main document.
To define an element in your main HTML document, define the element
from HTMLImports.whenReady(callback)
. callback
is invoked when
all imports in the document have finished loading.
<!DOCTYPE html>
<html>
<head>
<script src="bower_components/webcomponentsjs/webcomponents-lite.js">
</script>
<link rel="import" href="bower_components/polymer/polymer.html">
<title>Defining a Polymer Element from the Main Document</title>
</head>
<body>
<dom-module id="main-document-element">
<template>
<p>
Hi! I'm a Polymer element that was defined in the
main document!
</p>
</template>
<script>
HTMLImports.whenReady(function () {
Polymer({
is: 'main-document-element'
});
});
</script>
</dom-module>
<main-document-element></main-document-element>
</body>
</html>
Lifecycle callbacks
Polymer’s Base prototype implements the standard Custom Element lifecycle callbacks to perform tasks necessary for Polymer’s built-in features. Polymer in turn calls shorter-named lifecycle methods on your prototype.
Polymer adds an extra callback, ready
, which is invoked when Polymer has
finished creating and initializing the element’s local DOM.
Callback | Description |
---|---|
created |
Called when the element has been created, but before property values are
set and local DOM is initialized.
Use for one-time set-up before property values are set. Use instead of |
ready |
Called after property values are set and local DOM is initialized.
Use for one-time configuration of your component after local DOM is initialized. (For configuration based on property values, it may be preferable to use an observer.) |
attached |
Called after the element is attached to the document. Can be called multiple
times during the lifetime of an element. The first `attached` callback
is guaranteed not to fire until after `ready`.
Uses include accessing computed style information, and adding
document-level event listeners. (If you use declarative
event handling, such as annotated
event listeners or the
Use instead of |
detached |
Called after the element is detached from the document. Can be called
multiple times during the lifetime of an element.
Uses include removing event listeners added in Use instead of |
attributeChanged |
Called when one of the element's attributes is changed.
Use to handle attribute changes that don't correspond to declared properties. (For declared properties, Polymer handles attribute changes automatically as described in attribute deserialization.) Use instead of |
Example:
MyElement = Polymer({
is: 'my-element',
created: function() {
console.log(this.localName + '#' + this.id + ' was created');
},
ready: function() {
console.log(this.localName + '#' + this.id + ' has local DOM initialized');
},
attached: function() {
console.log(this.localName + '#' + this.id + ' was attached');
},
detached: function() {
console.log(this.localName + '#' + this.id + ' was detached');
},
attributeChanged: function(name, type) {
console.log(this.localName + '#' + this.id + ' attribute ' + name +
' was changed to ' + this.getAttribute(name));
}
});
Ready callback and local DOM initialization
The ready
callback is called when a Polymer element’s
local DOM has been initialized.
What is local DOM? Local DOM is a subtree of elements created and managed by your element. It’s separate from the element’s children, which are called light DOM for clarity. For more information, see Local DOM.
An element is ready when:
-
Its property values have been configured, with values data-bound from parents, deserialized from attribute values, or else set to their default value.
-
Its local DOM template has been instantiated.
-
All of the elements inside the element’s local DOM are ready, and have had their
ready
methods called.
Implement ready
when it’s necessary to manipulate an element’s
local DOM when the element is constructed.
ready: function() {
// access a local DOM element by ID using this.$
this.$.header.textContent = 'Hello!';
}
Note: This example uses Automatic node finding to access a local DOM element.
Within a given tree, ready
is generally called in document order,
but you should not rely on the ordering of initialization callbacks between
sibling elements, or between a host element and its light DOM children.
Initialization order and timing
The element’s basic initialization order for a given element is:
created
callback.- Local DOM initialized (This means that local DOM children are created,
their property values are set as specified in the template, and
ready
has been called on them). ready
callback.factoryImpl
callback.attached
callback.
Note that while the life cycle callbacks listed above will be called in the described order for any given element, the initialization timing between elements may vary depending on many factors, including whether or not the browser includes native support for web components.
Initialization timing for light DOM children
There are no guarantees about the initialization timing of light DOM children. In general elements are initialized in document order, so children are usually initialized after their parents.
For example, consider this light DOM for an element avatar-list
:
<avatar-list>
<my-photo class="photo" src="one.jpg">First photo</my-photo>
<my-photo class="photo" src="two.jpg">Second photo</my-photo>
</avatar-list>
<avatar-list>
is likely to have its ready
method called before the various
<my-photo>
elements do.
In addition, the user can add light children at any time after the parent element has been created. A well-designed element should handle having its light DOM manipulated at runtime.
To avoid timing issues, you can use the following strategies:
-
Handle light DOM children lazily. For example, a popup menu element may need to count its light DOM children. By counting its
children
when the menu is opened, it can handle the user adding and removing menu items with minimal overhead. -
To react when children are added and removed, use the
observeNodes
method.
Initialization timing for local DOM children
In terms of local DOM and initialization timing, local DOM children are created,
their property values are set as specified in the template, and ready
is
called on them before their parent’s ready
callback is called.
There are two caveats:
-
dom-repeat
anddom-if
templates create DOM asynchronously after their properties are updated. For example, if you have adom-repeat
in your element’s local DOM, theready
callback is invoked before thedom-repeat
finishes creating its instances.If you need to know when a
dom-repeat
ordom-if
creates or removes template instances, listen for itsdom-change
event. Seedom-change
event for details. -
Polymer guarantees that local DOM children have their
ready
callback called before their parent’s; however, it cannot guarantee the same thing for theattached
callback. This is one fundamental difference between native behavior and polyfill behavior.
Initialization timing for siblings
There are no guarantees with regard to initialization timing between sibling elements.
This means that siblings may become ready
in any order.
For accessing sibling elements when an element initializes, you can call async
from inside the attached
callback:
attached: function() {
this.async(function() {
// access sibling or parent elements here
});
}
Registration callback
Polymer also provides two registration-time callbacks, beforeRegister
and registered
.
Use the beforeRegister
callback to transform an element’s prototype before
registration. This is useful when registering an element using an ES6 class,
as described in the article, Building web components using ES6 classes.
You can implement the registered
callback to perform one-time initialization
when an element is registered. This is primarily useful when implementing
behaviors.
Static attributes on host
If a custom element needs HTML attributes set on it at create-time, the attributes may
be declared in a hostAttributes
property on the prototype, where keys are the
attribute names and values are the values to be assigned. Values should
typically be provided as strings, as HTML attributes can only be strings;
however, the standard serialize
method is used to convert values to strings,
so true
will serialize to an empty attribute, and false
will result in no
attribute set, and so forth (see Attribute serialization for more
details).
Example:
<script>
Polymer({
is: 'x-custom',
hostAttributes: {
'string-attribute': 'Value',
'boolean-attribute': true
tabindex: 0
}
});
</script>
Results in:
<x-custom string-attribute="Value" boolean-attribute tabindex="0"></x-custom>
Note: The class
attribute can’t be configured using hostAttributes
.
Behaviors
Elements can share code in the form of behaviors, which can define properties, lifecycle callbacks, event listeners, and other features.
For more information, see Behaviors.
Class-style constructor
If you want to set up your custom element’s prototype chain but not register
it immediately, you can use the Polymer.Class
function. Polymer.Class
takes
the same prototype argument as the Polymer
function, and sets up the
prototype chain, but does not register the element. Instead it returns a
constructor that can be passed to document.registerElement
to register your
element with the browser, and after which can be used to instantiate new
instances of your element via code.
If you want to define and register the custom element in one step, use the
Polymer
function.
Example:
var MyElement = Polymer.Class({
is: 'my-element',
// See below for lifecycle callbacks
created: function() {
this.textContent = 'My element!';
}
});
document.registerElement('my-element', MyElement);
// Equivalent:
var el1 = new MyElement();
var el2 = document.createElement('my-element');
Polymer.Class
is designed to provide similar ergonomics to a speculative future
where an ES6 class may be defined and provided to document.registerElement
.