W3C home > Mailing lists > Public > public-webapps@w3.org > January to March 2015

RE: Defining a constructor for Element and friends

From: Domenic Denicola <d@domenic.me>
Date: Sat, 10 Jan 2015 01:01:01 +0000
To: Anne van Kesteren <annevk@annevk.nl>, WebApps WG <public-webapps@w3.org>, "www-dom@w3.org" <www-dom@w3.org>
Message-ID: <CY1PR0501MB1369348CEE85464195E560B2DF450@CY1PR0501MB1369.namprd05.prod.outlook.com>
OK, so I've thought about this a lot, and there was some discussion on an unfortunately-TC39-private thread that I want to put out in the open. In [1] I outlined some initial thoughts, but that was actually a thread on a different topic, and my thinking has evolved.
[1]: http://lists.w3.org/Archives/Public/public-webapps/2015JanMar/0035.html

I was writing up my ideas in an email but it kind of snowballed into something bigger so now it's a repo: https://github.com/domenic/element-constructors

One primary concern of mine is the one you mention:

> whether it is acceptable to have an element whose name is "a", namespace is the HTML namespace, and interface is Element

I do not really think this is acceptable, and furthermore I think it is avoidable.

In the private thread Boris suggested a design where you can do `new Element(localName, namespace, prefix)`. This seems necessary to explain how `createElementNS` works, so we do want that. He also suggested the following invariants:

1.  The localName and namespace of an element determine its set of internal slots.
2.  The return value of `new Foo` has `Foo.prototype` as the prototype.

I agree we should preserve these invariants, but added a few more to do with keeping the existing (localName, namespace) <-> constructor links solid.

I've outlined the added invariants in the readme of the above repo. Other points of interest:

- Explainer for a very-recently-agreed-upon ES6 feature that helps support the design: https://github.com/domenic/element-constructors/blob/master/new-target-explainer.md
- Jump straight to the code: https://github.com/domenic/element-constructors/blob/master/element-constructors.js
- Jump straight to the examples of what works and what doesn't: https://github.com/domenic/element-constructors/blob/master/element-constructors.js#L194

One ugly point of my design is that the constructor signature is `new Element(localName, document, namespace, prefix)`, i.e. I require the document to be passed in. I am not sure this is necessary but am playing it safe until someone with better understanding tells me one way or the other. See https://github.com/domenic/element-constructors/issues/1 for that discussion.


As for how this applies to custom elements, in the private thread Boris asked: 

> what is the use case for producing something that extends HTMLImageElement (and presumably has its internal slots?) but doesn't have "img" as the tag name and hence will not have anything ever look at those internal slots?

Elsehwere on this thread or some related one IIRC he pointed out code that looks at the local name, finds "img", and casts to the C++ backing representation of HTMLImageElement. So from what I am gathering in his view the parts of the platform that treat <img> elements specially currently work by checking explicitly that something has local name "img" (and HTML namespace).

From a naïve authoring point of view that seems suboptimal. I'd rather be able to do `class MyImg extends HTMLImageElement { constructor(document) { super(document); } }` and have MyImg instances treated specially by the platform in all the ways "img" currently is.

Or, for an easier example, I'd like to be able to do `class MyQ extends HTMLQuoteElement { constructor(document) { super(document); } }` and have `(new MyQ()).cite` actually work, instead of throw a "cite getter incompatible with MyQ" error because I didn't get the HTMLQuoteElement internal slots.

The logical extension of this, then, is that if after that `document.registerElement` call I do `document.body.innerHTML = <my-q cite="foo">blah</my-q>` I'd really like to see `document.querySelector("my-q").cite` return `"foo"`.

However this idea that we'd like custom elements which inherit from existing elements to have their internal slots ties in to the whole upgrading mess, which seems quite hard to work around. So maybe it is not a good idea? On the other hand upgrading might be borked no matter what, i.e. it might not be possible at all to make upgraded elements behave anything like parsed-from-scratch elements. (I am planning to think harder about the upgrading problem but I am not hopeful.)

Received on Saturday, 10 January 2015 01:01:36 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 18:14:43 UTC