You're viewing an older version of Polymer. Please see Polymer 2.0 for the latest.

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.

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. The factoryImpl method is not called if the element is created from markup by the HTML parser, or if the element is created using document.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.

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">

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>

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 createdCallback.

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 listeners object, Polymer automatically adds listeners on attach and removes them on detach.)

Use instead of attachedCallback.

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 attached.

Use instead of detachedCallback.

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 attributeChangedCallback.

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));
      }

    });

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 registered 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 after the local DOM has been 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.

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, assuming they are registered).
  • ready callback.
  • factoryImpl callback.
  • attached callback.

Local DOM children only have ready called if they are registered custom elements. If a local DOM child is registered later, its created and ready methods are called when that child upgrades, without delaying the host's remaining callbacks. Importing sources before they are used ensures that elements are created in order.

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.

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.

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 and dom-if templates create DOM asynchronously after their properties are updated. For example, if you have a dom-repeat in your element's local DOM, the ready callback is invoked before the dom-repeat finishes creating its instances.

    If you need to know when a dom-repeat or dom-if creates or removes template instances, listen for its dom-change event. See dom-change event for details.

  • Polymer guarantees that local DOM children have their ready callback called before their parent's ready callback; however, it cannot guarantee that local DOM children have their attached callback called before their parent's attached callback. This is one fundamental difference between native behavior and polyfill behavior.

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
      });
    }

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.

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.

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.

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.