Home Start Get started Quick tour of Polymer Install the Release Candidate Polymer Feature overview What's new About Polymer 2.0 Upgrade guide Hybrid elements Release notes Custom elements Custom element concepts Define an element Declare properties Shadow DOM & styling Shadow DOM concepts DOM templating Style shadow DOM Custom CSS properties Events Handle and fire events Gesture events Data system Data system concepts Work with object and array data Observers and computed properties Data binding Helper elements Tools Tools overview Polymer CLI Document your elements Test your elements Optimize for production Advanced tools Services polymer.json specification Node support Resources Browser compatibility Glossary API Reference API Reference App Toolbox What's in the box? Using the Toolbox App templates Responsive app layout Routing Localization App storage Service worker Serve your app Case study Shop News Blog Community Feature overview
What's new
About Polymer 2.0 Upgrade guide Hybrid elements Release notes
Custom elements
Custom element concepts Define an element Declare properties
Shadow DOM & styling
Shadow DOM concepts DOM templating Style shadow DOM Custom CSS properties
Events
Handle and fire events Gesture events
Data system
Data system concepts Work with object and array data Observers and computed properties Data binding Helper elements
Tools
Tools overview Polymer CLI Document your elements Test your elements Optimize for production Advanced tools Services polymer.json specification Node support
Resources
Browser compatibility Glossary
API Reference
API Reference

Hybrid elements are Polymer elements designed to run under both Polymer 1.x and Polymer 2.x. Polymer 2 provides a backwards-compatible API for hybrid elements.

Implementing a hybrid element requires some extra work, including maintaining multiple sets of bower dependencies, testing on both Polymer 1 and Polymer 2. Build hybrid elements if you're creating a set of reusable elements and need to support customers using both Polymer 1.x and Polymer 2.x. You may also find hybrid elements useful if you're trying to port a large application.

Polymer CLI supports installing and testing with multiple versions of your bower dependencies, so you can test your hybrid elements against multiple versions of Polymer. For an overview, see Manage dependencies for hybrid elements.

Hybrid element overview

A hybrid element is defined using a 2.x-style DOM template and a 1.x-style Polymer() function

The Polymer function sets up the prototype chain for your custom element, so you cannot set up your own prototype chain.

  • In 1.x, your prototype is chained to the Polymer Base prototype (which provides Polymer value-added features).

  • In 2.0, Polymer uses your prototype to create a new class that extends Polymer.LegacyElement.

Hybrid elements must use a compatible subset of the 1.x API. (Version-specific API calls can be conditionalized.)

You can use Polymer 1.x style behaviors to share code between elements, as long as they follow the same API restrictions as hybrid elements.

Working with DOM

Hybrid elements need to run under Polymer 2.0—which uses the newer shadow DOM v1 specification, and and Polymer 1.x, which uses the earlier shadow DOM v0 specification.

  • When writing the DOM template for a hybrid element, use the shadow DOM v1 style <slot> element and ::slotted selector.

  • When manipulating DOM elements at runtime, use the Polymer.dom APIs for backward compatibility.

DOM template and styling

Hybrid elements must use the shadow DOM v1 style <slot> element, and ::slotted() CSS selector in place of the <content> element and ::content selector from shadow DOM v0.

Note that <slot> is more restrictive than the v0 <content> mechanism:

  • Insertion points that selected content using <content select="selector"> must be changed to named slots: <slot name="slot_name">. Note that in shadow DOM v1, distributed content can only be selected by slot name, not by tag name, attributes or CSS classes.

  • Users of your element must use the matching new slot="slot_name" attribute to distribute content into a named slot.

  • Any ::content CSS selectors must be replaced with ::slotted(selector), where selector is compound selector that identifies a top-level distributed child. That is, ::slotted(.foo) is equivalent to ::content > .foo.

    In shadow DOM v1, you cannot select a descendant of a top-level distributed child. For example, ::slotted(*) .child does not work. No descendant selectors can follow the ::slotted() selector.

When running in Polymer 1.x, <slot> elements are re-written at runtime into the equivalent <content> elements and ::content style rules, to work with shadow DOM v0.

As written:

<dom-module id="x-forward-compat">
  <template>
    <style>
      #container ::slotted(.foo) {
        color: red;
      }
      #namedContainer ::slotted(*) {
        border: 2px solid black;
      }
    </style>
    <div id="namedContainer">
      <slot name="named"></slot>
    </div>
    <div id="container">
      <slot></slot>
    </div>
  </template>
</dom-module>

After runtime transform:

<dom-module id="x-forward-compat">
  <template>
    <style>
      #container ::content > .foo {
        color: red;
      }
      #namedContainer ::content > * {
        border: 2px solid black;
      }
    </style>
    <div id="namedContainer">
      <content select="[slot=named]"></content>
    </div>
    <div id="container">
      <content></content>
    </div>
  </template>
</dom-module>

Note that Polymer doesn't transform code that uses this element. Anywhere you're using x-forward-compat, you need to use the new slot syntax:

<x-forward-compat>
  <h2 slot="named">I'm the named content</h2>
  <span>This content goes to the default slot.</span>
</x-forward-compat>

Default slot behavior

For native shadow DOM v1, a default slot doesn't match children with an explicit slot name (that is, a slot="slot_name" attribute).

When running in 1.x mode, a default <slot> element is translated to a default <content> element, which accepts all distributed children that haven't matched a previous <content> element. This means that for hybrid elements, all named slots must come before any unnamed slots in the DOM.

For example, given this set of slots:

<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>

Content with slot="footer" is distributed to the default slot in 1.x, but distributed to the last slot in 2.x.

DOM APIs

When working with the DOM imperatively, use the Polymer 1.x APIs, such as Polymer.dom, observeNodes, and getContentChildNodes.

Note that the initial distribution of light DOM children into slots may be delayed under the polyfill, as described in the discussion of the ready callback.

Also note that Polymer.dom.flush does not flush observeNodes callbacks in 2.0. This is most likely to affect unit tests using Polymer.dom.flush to ensure that shadow DOM children have been distributed.

This change is because observeNodes uses the native slotchange event where possible and there is no mechanism to force the event to fire. Instead, there is a flush method on the observer object.

x-sample.html

attached: function() {
  this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
},
_onNodesChange: function() {
  this.count = Polymer.dom(this).children.length;
}

Test code, before

Polymer.dom(myElement).appendChild(document.createElement('div'));
Polymer.dom.flush();
// test some condition that should be true after the observeNodes callback fires
assert.equal(myElement.count, 1, 'child count should be 1');

Test code, after

Polymer.dom(myElement).appendChild(document.createElement('div'));
myElement._observer.flush ? overlay._observer.flush() : Polymer.dom.flush();
// test some condition that should be true after the observeNodes callback fires
assert.equal(myElement.count, 1, 'child count should be 1');

Version-specific code

In some cases, you may need to run different code depending on which version of Polymer is in use. One easy way to test the version is to test for the existence of the Polymer.Element constructor, which is only used in Polymer 2.

if (Polymer.Element) {
  // version 2 code
} else {
  // version 1 code
}

Manage dependencies for hybrid elements

For testing Polymer elements, Polymer CLI supports installing multiple versions of bower dependencies. These versions are called variants. The components' default dependencies are listed in the standard dependencies and devDependencies sections. The default dependencies should use version ranges that include all versions supported by the component (typically, 1.7.1 or higher for Polymer itself).

Other sets are listed in a special variants section. For example:

  "dependencies": {
    "polymer": "Polymer/polymer#>=1.7.1 <3.0.0"
  },
  "devDependencies": {
    "iron-component-page": "PolymerElements/iron-component-page#>=1.0.0 <3.0.0",
    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#>=1.0.0 <3.0.0",
    "test-fixture": "PolymerElements/test-fixture#>=1.0.0 <3.0.0",
    "web-component-tester": "^6.0.0",
    "webcomponentsjs": "webcomponents/webcomponentsjs#>=0.7.0 <2.0.0"
  },
  "variants": {
    "1.x": {
      "dependencies": {
        "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
        "polymer": "Polymer/polymer#^1.0.0"
      },
      "devDependencies": {
        "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
        "paper-styles": "PolymerElements/paper-styles#^1.0.0",
        "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
        "test-fixture": "PolymerElements/test-fixture#^1.0.0",
        "web-component-tester": "^4.0.0",
        "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
      }
    }
  },

In the example above, the default dependencies match either 1.x or 2.x, while the 1.x variant matches 1.x.

Run the following command to install both sets of dependencies:

polymer install --variants

Other CLI commands like polymer serve and polymer test can run against the default dependencies, as well as any variants. For example, polymer serve serves both versions at the same time, from different ports. Likewise, polymer test runs your test suites using each of the installed variants.

When a user installs a hybrid element, only the default dependencies are considered. The variants are used purely for local development and testing.