or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bundle-usage.mddynamic-loading.mdes5-adapter.mdevent-system.mdindex.md
tile.json

es5-adapter.mddocs/

ES5 Custom Elements Adapter

The custom-elements-es5-adapter.js provides compatibility for ES5-style custom element classes when using transpiled code with native Custom Elements implementations.

Capabilities

ES5 Adapter Script

Compatibility shim that allows ES5-style custom element classes to work with native Custom Elements.

/**
 * Include custom-elements-es5-adapter.js before defining custom elements
 * Automatically wraps ES5 constructor functions for native Custom Elements compatibility
 * Required when serving ES5 code to browsers with native Custom Elements support
 */
// <script src="custom-elements-es5-adapter.js"></script>

// The adapter automatically patches the native customElements.define method
// No direct API - works transparently with custom element definitions

Usage Examples:

<!-- Load adapter first -->
<script src="custom-elements-es5-adapter.js"></script>

<!-- Then load ES5-compiled custom elements -->
<script src="my-element-es5.js"></script>

<!-- Elements work normally -->
<my-element></my-element>

Compatibility Problem

Understanding when and why the ES5 adapter is needed.

/**
 * Problem: ES5 classes cannot properly extend native ES6 classes like HTMLElement
 * Native Custom Elements API requires ES6 class syntax for proper prototype chain
 * 
 * Without adapter:
 * - ES5 custom elements fail on browsers with native Custom Elements
 * - TypeError: Class constructor HTMLElement cannot be invoked without 'new'
 * 
 * With adapter:
 * - ES5 constructor functions are automatically wrapped
 * - Proper prototype chain is established
 * - Custom elements work correctly
 */

// ES5 style (problematic without adapter)
function MyElement() {
  // This fails with native Custom Elements
  return HTMLElement.call(this) || this;
}
MyElement.prototype = Object.create(HTMLElement.prototype);

// ES6 style (works natively)
class MyElement extends HTMLElement {
  constructor() {
    super();
  }
}

Automatic Wrapping

The adapter automatically detects and wraps ES5 constructor functions.

/**
 * The adapter patches customElements.define to detect ES5 constructors
 * When an ES5 constructor is detected, it's automatically wrapped
 * The wrapping preserves the original constructor's behavior
 */

// Original ES5 custom element
function MyButton() {
  var self = HTMLElement.call(this) || this;
  self.addEventListener('click', self._handleClick);
  return self;
}
MyButton.prototype = Object.create(HTMLElement.prototype);
MyButton.prototype._handleClick = function() {
  console.log('Clicked!');
};

// With adapter loaded, this works on native Custom Elements:
customElements.define('my-button', MyButton);

Usage Examples:

// Transpiled Babel/TypeScript output (ES5)
var MyElement = /** @class */ (function (_super) {
  __extends(MyElement, _super);
  function MyElement() {
    var _this = _super.call(this) || this;
    _this.attachShadow({mode: 'open'});
    return _this;
  }
  return MyElement;
}(HTMLElement));

// Works with adapter
customElements.define('my-element', MyElement);

Browser Compatibility

When to include the ES5 adapter in your application.

/**
 * Include adapter when:
 * - Serving ES5/transpiled code to all browsers
 * - Target browsers include both ES5-only and native Custom Elements support
 * - Using build tools that compile to ES5 (Babel, TypeScript ES5 target)
 * 
 * Skip adapter when:
 * - Serving ES6+ code with differential loading
 * - Only targeting browsers without native Custom Elements (IE11, old browsers)
 * - Using only the webcomponents-bundle.js (includes adapter functionality)
 */

// Browser support matrix:
// - Chrome 54+: Native Custom Elements, needs adapter for ES5 code
// - Safari 10.1+: Native Custom Elements, needs adapter for ES5 code  
// - Firefox 63+: Native Custom Elements, needs adapter for ES5 code
// - IE 11: No native Custom Elements, polyfill handles ES5 code

Integration with Polyfills

How the adapter works with other Web Components polyfills.

/**
 * Adapter loading order:
 * 1. Load adapter before custom element definitions
 * 2. Adapter can be loaded before or after other polyfills
 * 3. If using webcomponents-bundle.js, adapter is already included
 */

// Correct loading order
// <script src="custom-elements-es5-adapter.js"></script>
// <script src="webcomponents-loader.js"></script> <!-- Optional -->
// <script src="my-elements.js"></script>

// Or with bundle (adapter included)
// <script src="webcomponents-bundle.js"></script>
// <script src="my-elements.js"></script>

Usage Examples:

<!-- Differential loading approach -->
<script type="module" src="my-element-es6.js"></script>
<script nomodule>
  // For older browsers, load adapter + ES5 code
  document.write('<script src="custom-elements-es5-adapter.js"><\/script>');
  document.write('<script src="my-element-es5.js"><\/script>');
</script>

<!-- Universal ES5 approach -->
<script src="custom-elements-es5-adapter.js"></script>
<script src="my-element-es5.js"></script>

Build Tool Integration

Webpack Configuration

// webpack.config.js
module.exports = {
  entry: {
    'es5-adapter': '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js',
    'app': './src/app.js'
  },
  // Ensure adapter loads before app code
};

TypeScript Configuration

// tsconfig.json for ES5 output requiring adapter
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es2015", "dom"],
    "experimentalDecorators": true
  }
}

Babel Configuration

// .babelrc for ES5 output requiring adapter
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["ie 11"]
      }
    }]
  ]
}

Performance Considerations

File Size:

  • custom-elements-es5-adapter.js: ~2KB minified
  • Minimal overhead for the compatibility layer

Runtime Performance:

  • No performance impact on browsers using polyfilled Custom Elements
  • Minimal wrapping overhead on browsers with native Custom Elements
  • Only affects custom element construction, not ongoing operations

Loading Strategy:

  • Load adapter early to ensure it's available when custom elements are defined
  • Can be inlined for critical path optimization
  • Consider conditional loading based on browser detection

Error Handling

// The adapter handles common ES5/ES6 compatibility issues automatically
// Manual error handling is rarely needed

try {
  customElements.define('my-element', MyES5Element);
} catch (error) {
  console.error('Custom element definition failed:', error);
  // Fallback to non-custom element implementation
}

Common issues the adapter resolves:

  • Constructor invocation errors with native Custom Elements
  • Prototype chain problems in ES5 compiled code
  • Property descriptor conflicts between ES5 and ES6 classes