1. Experimental()
void registerElement(String tag, Type customElementClass, { String extendsTag })

Register a custom subclass of Element to be instantiatable by the DOM.

This is necessary to allow the construction of any custom elements.

The class being registered must either subclass HtmlElement or SvgElement. If they subclass these directly then they can be used as:

class FooElement extends HtmlElement{
   void created() {
     print('FooElement created!');
   }
}

main() {
  document.registerElement('x-foo', FooElement);
  var myFoo = new Element.tag('x-foo');
  // prints 'FooElement created!' to the console.
}

The custom element can also be instantiated via HTML using the syntax <x-foo></x-foo>

Other elements can be subclassed as well:

class BarElement extends InputElement{
   void created() {
     print('BarElement created!');
   }
}

main() {
  document.registerElement('x-bar', BarElement);
  var myBar = new Element.tag('input', 'x-bar');
  // prints 'BarElement created!' to the console.
}

This custom element can also be instantiated via HTML using the syntax <input is="x-bar"></input>

Source

@Experimental()
/**
 * Register a custom subclass of Element to be instantiatable by the DOM.
 *
 * This is necessary to allow the construction of any custom elements.
 *
 * The class being registered must either subclass HtmlElement or SvgElement.
 * If they subclass these directly then they can be used as:
 *
 *     class FooElement extends HtmlElement{
 *        void created() {
 *          print('FooElement created!');
 *        }
 *     }
 *
 *     main() {
 *       document.registerElement('x-foo', FooElement);
 *       var myFoo = new Element.tag('x-foo');
 *       // prints 'FooElement created!' to the console.
 *     }
 *
 * The custom element can also be instantiated via HTML using the syntax
 * `<x-foo></x-foo>`
 *
 * Other elements can be subclassed as well:
 *
 *     class BarElement extends InputElement{
 *        void created() {
 *          print('BarElement created!');
 *        }
 *     }
 *
 *     main() {
 *       document.registerElement('x-bar', BarElement);
 *       var myBar = new Element.tag('input', 'x-bar');
 *       // prints 'BarElement created!' to the console.
 *     }
 *
 * This custom element can also be instantiated via HTML using the syntax
 * `<input is="x-bar"></input>`
 *
 */
void registerElement(String tag, Type customElementClass,
    {String extendsTag}) {
  // Hack to setup an interceptor for HTMLElement so it isn't changed when a custom element is created.
  var jsHTMLElementPrototype = js.JsNative.getProperty(js.JsNative.getProperty(js.context, 'HTMLElement'),'prototype');
  _blink.Blink_Utils.defineInterceptor(jsHTMLElementPrototype, HtmlElement.instanceRuntimeType);

  // Figure out which DOM class is being extended from the user's Dart class.
  var classMirror = reflectClass(customElementClass);

  var locationUri = classMirror.location.sourceUri.toString();
  if (locationUri == 'dart:html' || locationUri == 'dart:svg') {
    throw new DomException.jsInterop("HierarchyRequestError: Cannot register an existing dart:html or dart:svg type.");
  }

  if (classMirror.isAbstract) {
    throw new DomException.jsInterop("HierarchyRequestError: Cannot register an abstract class.");
  }

  var jsClassName = _getJSClassName(classMirror);
  if (jsClassName == null) {
    // Only components derived from HTML* can be extended.
    throw new DomException.jsInterop("HierarchyRequestError: Only HTML elements can be customized.");
  }

  var customClassType = _getDartHtmlClassName(classMirror);

  if (extendsTag != null) {
    var nativeElement = document.createElement(extendsTag);

    // Trying to extend a native element is it the Dart class consistent with the
    // extendsTag?
    if (nativeElement.runtimeType != customClassType.reflectedType) {
      var nativeElementClassMirror = reflectClass(nativeElement.runtimeType);
      var customClassNativeElement = MirrorSystem.getName(customClassType.simpleName);
      var extendsNativeElement = MirrorSystem.getName(nativeElementClassMirror.simpleName);
      throw new DomException.jsInterop("HierarchyRequestError: Custom class type ($customClassNativeElement) and extendsTag class ($extendsNativeElement) don't match .");
    }
  } else if (customClassType.reflectedType != HtmlElement && customClassType.reflectedType != svg.SvgElement) {
    var customClassName = MirrorSystem.getName(classMirror.simpleName);
    var customClassElement = MirrorSystem.getName(customClassType.simpleName);
    throw new DomException.jsInterop("HierarchyRequestError: Custom element $customClassName is a native $customClassElement should be derived from HtmlElement or SvgElement.");
  }

  if (_hasCreatedConstructor(classMirror)) {
    // Start the hookup the JS way create an <x-foo> element that extends the
    // <x-base> custom element. Inherit its prototype and signal what tag is
    // inherited:
    //
    //     var myProto = Object.create(HTMLElement.prototype);
    //     var myElement = document.registerElement('x-foo', {prototype: myProto});
    var baseElement = js.JsNative.getProperty(js.context, jsClassName);
    if (baseElement == null) {
      // Couldn't find the HTML element so use a generic one.
      baseElement = js.JsNative.getProperty(js.context, 'HTMLElement');
    }
    var elemProto = js.JsNative.callMethod(js.JsNative.getProperty(js.context, 'Object'), "create", [js.JsNative.getProperty(baseElement, 'prototype')]);

    // TODO(terry): Hack to stop recursion re-creating custom element when the
    //              created() constructor of the custom element does e.g.,
    //
    //                  MyElement.created() : super.created() {
    //                    this.innerHtml = "<b>I'm an x-foo-with-markup!</b>";
    //                  }
    //
    //              sanitizing causes custom element to created recursively
    //              until stack overflow.
    //
    //              See https://github.com/dart-lang/sdk/issues/23666
    int creating = 0; // TODO(jacobr): I think I broke thise case. Will fix monday.

    // If any JS code is hooked we want to call it too.
    var oldCreatedCallback = js.JsNative.getProperty(elemProto, 'createdCallback');
    var oldAttributeChangedCallback = js.JsNative.getProperty(elemProto, 'attributeChangedCallback');
    var oldAttachedCallback = js.JsNative.getProperty(elemProto, 'attachedCallback');
    var oldDetachedCallback = js.JsNative.getProperty(elemProto, 'detachedCallback');

    js.JsNative.setProperty(elemProto, 'createdCallback', js.allowInteropCaptureThis(($this) {
      // The created callback has already been called by the very act of passing a JS
      // custom element from JS to Dart.

      //  Make element's interceptor a CustomElementClass.
      _blink.Blink_Utils.setInstanceInterceptorCustomUpgrade($this);

      if (oldCreatedCallback != null)
        oldCreatedCallback.apply([], thisArg: $this);
    }));
    js.JsNative.setProperty(elemProto, 'attributeChangedCallback', js.allowInteropCaptureThis(($this, attrName, oldVal, newVal) {
      $this.attributeChanged(attrName, oldVal, newVal);

      if (oldAttributeChangedCallback != null)
        oldAttributeChangedCallback.apply([], thisArg: $this);
    }));
    js.JsNative.setProperty(elemProto, 'attachedCallback', js.allowInteropCaptureThis(($this) {
      $this.attached();

      if (oldAttachedCallback != null)
        oldAttachedCallback.apply([], thisArg: $this);
    }));
    js.JsNative.setProperty(elemProto, 'detachedCallback', js.allowInteropCaptureThis(($this) {
      $this.detached();

      if (oldDetachedCallback != null)
        oldDetachedCallback.apply([], thisArg: $this);
    }));
    // document.registerElement('x-foo', {prototype: elemProto, extends: extendsTag});
    var jsMap = new js.JsObject.jsify({'prototype': elemProto, 'extends': extendsTag});
    _blink.Blink_Utils.defineInterceptorCustomElement(elemProto, customElementClass);
    js.JsNative.callMethod(document, 'registerElement', [tag, jsMap]);
  }
}