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}) {
  // 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')]);

    // Remember for any upgrading done in wrap_jso.
    addCustomElementType(tag, customElementClass, extendsTag);

    // 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;

    // If any JS code is hooked we want to call it too.
    var oldCreatedCallback = elemProto['createdCallback'];
    var oldAttributeChangedCallback = elemProto['attributeChangedCallback'];
    var oldAttachedCallback = elemProto['attachedCallback'];
    var oldDetachedCallback = elemProto['detachedCallback'];

    // TODO(jacobr): warning:
    elemProto['createdCallback'] = js.JsNative.withThis(($this) {
      if (_getJSClassName(reflectClass(customElementClass).superclass) != null && creating < 2) {
        creating++;

        var dartClass;
        try {
          if (extendsTag != null) {
            // If we're extending a native element then create that element.
            // Then upgrade that element to the customElementClass through
            // normal flow.
            dartClass = document.createElement(extendsTag);
            js.setDartHtmlWrapperFor($this, dartClass);
            dartClass.blink_jsObject = $this;
          }

          // Upgrade to the CustomElement Dart class.
          dartClass = _blink.Blink_Utils.constructElement(customElementClass, $this);
        } catch (e) {
          // Got a problem make it an HtmlElement and rethrow the error.
          dartClass = HtmlElement.internalCreateHtmlElement();
          // We need to remember the JS object (because constructElement failed
          // it normally sets up the blink_jsObject.
          dartClass.blink_jsObject = $this;

          // Mark to only try this once don't try upgrading from HtmlElement
          // to the user's Dart class - we had a problem.
          dartClass._badUpgrade();
          throw e;
        } finally {
          // Need to remember the Dart class that was created for this custom so
          // return it and setup the blink_jsObject to the $this that we'll be working
          // with as we talk to blink.
          js.setDartHtmlWrapperFor($this, dartClass);

          creating--;
        }
      }

      if (oldCreatedCallback != null)
        oldCreatedCallback.apply([], thisArg: unwrap_jso($this));
    });
    elemProto['attributeChangedCallback'] = new js.JsFunction.withThis(($this, attrName, oldVal, newVal) {
      $this.attributeChanged(attrName, oldVal, newVal);

      if (oldAttributeChangedCallback != null)
        oldAttributeChangedCallback.apply([], thisArg: unwrap_jso($this));
    });
    elemProto['attachedCallback'] = new js.JsFunction.withThis(($this) {
      $this.attached();

      if (oldAttachedCallback != null)
        oldAttachedCallback.apply([], thisArg: unwrap_jso($this));
    });
    elemProto['detachedCallback'] = new js.JsFunction.withThis(($this) {
      $this.detached();

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