CustomElementsをextendsすることはできないぽい

再現コード

Web Components のはなし。

去年8月時点に書かれた Custom Elements: defining new elements in HTML にも掲載されていたのと同じような感じのコードをChrome上で実行したらエラーになった。

var XFooProto = Object.create(HTMLElement.prototype);
var XFoo = document.registerElement('x-foo', {
  prototype: XFooProto
});

var XFooExtendedProto = Object.create(HTMLElement.prototype);
var XFooExtended = document.registerElement('x-foo-extended', {
  prototype: XFooExtendedProto,
  extends: 'x-foo'
});

エラーは次のとおりだ。

Error: Failed to execute 'registerElement' on 'Document': Registration failed for type 'x-foo-extended'. The tag name specified in 'extends' is a custom element name. Use inheritance instead.

extends に CustomElements のタグ名が指定されてるからアカン、代わりに inheritance 使えって。

Google Chrome : Version 37.0.2062.94 & Version 39.0.2146.0 canary

そうなのかー

Blink の該当コード漁ったらたしかにそのような処理になっていた。

if (extendsProvidedAndNonNull) {
    localName = extends.lower();

    if (!Document::isValidName(localName)) {
        CustomElementException::throwException(CustomElementException::ExtendsIsInvalidName, type, exceptionState);
        return false;
    }
    if (CustomElement::isValidName(localName)) {
        CustomElementException::throwException(CustomElementException::ExtendsIsCustomElementName, type, exceptionState);
        return false;
    }
} else {
    if (namespaceURI == SVGNames::svgNamespaceURI) {
        CustomElementException::throwException(CustomElementException::ExtendsIsInvalidName, type, exceptionState);
        return false;
    }
    localName = type;
}

CustomElements として Valid な名前だったら CustomElementException::ExtendsIsCustomElementName が投げられている。

原典

W3C Editor's Draft 5 September 2014 Custom Elements で確認した。

  1. If PROTOTYPE is null, let PROTOTYPE be the result of invoking Object.create with HTMLElement's interface prototype object as only argument
  2. Let NAME be EXTENDS
  3. Let ERROR be the result of running the element registration algorithm with DOCUMENT, TYPE, PROTOTYPE, and NAME as arguments
  4. If ERROR is InvalidType, throw a SyntaxError and stop.
  5. If ERROR is not None, throw a NotSupportedError and stop.

NotSupportedError ということなので、この辺でひっかかってると思うのだが。肝心の element registration algorithm をみると...

Input
DOCUMENT, the document
TYPE, the custom element type of the element being registered
PROTOTYPE, the custom element prototype
NAME, a local name, optional
Output
...中略...
8. If NAME was provided and is not null:
Let BASE be the element interface for NAME and NAMESPACE
If BASE does not exist or is an interface for a custom element, set ERROR to InvalidName and stop.

たぶんこのへんで止まってるのだろう。

単に prototype に渡すオブジェクトを継承させよう

ということで extends というか JavaScript 伝統の継承的アレで prototype の実装を引き継がせておいて、Document#registerElementextends オプションは指定しないというのが正解になるっぽい。

現場からは以上です。