[Custom Elements] Error in IE11: The custom element being constructed was not registered with `customElements`.
Description
CustomElementInternals.js causes an error in IE.
Live Demo
Run this pen in IE11:
https://codepen.io/anon/pen/dJerjB
Steps to Reproduce
See the pen in IE11. You can use http://crossbrowsertesting.com to test the pen in IE11 for free (click "Change View" in the pen to see a link to "Open on CrossBrowserTesting".
Expected Results
no error
Actual Results
You'll get an error
SCRIPT5022: The custom element being constructed was not registered with `customElements`.
pointing to this line:
https://github.com/webcomponents/custom-elements/blob/134c58e6b21a6788f18b715ac5d70ede0cad779f/src/CustomElementInternals.js#L275
(on column 7, and based on the source map)
Browsers Affected
- [ ] Chrome
- [ ] Firefox
- [ ] Edge
- [ ] Safari 9
- [ ] Safari 8
- [x] IE 11
Versions
-
[email protected], using https://unpkg.com/@webcomponents/[email protected]/webcomponents-sd-ce.js
The other error that also appears in the console is reported in https://github.com/webcomponents/shadydom/issues/207.
The pen is quite big. Could you provide a minimal reproduction case that shows the issue? That eases the debugging process.
+1 The same here.
Simple use case:
- including custom-elements.js polyfill
- defining custom element per
class extends HTMLElement {andcustomElements.define - using it
I found this chunk in there which seems to prevent the original constructor from being found:
var _fixBabelExtend = function (O) {
var gOPD = O.getOwnPropertyDescriptor,
gPO = O.getPrototypeOf || function (o) {
return o.__proto__;
},
sPO = O.setPrototypeOf || function (o, p) {
o.__proto__ = p;
return o;
},
construct$$1 = typeof Reflect === 'object' ? _Reflect$construct : function (Parent, args, Class) {
var Constructor,
a = [null];
a.push.apply(a, args);
Constructor = Parent.bind.apply(Parent, a);
return sPO(new Constructor(), Class.prototype);
};
return function fixBabelExtend(Class) {
var Parent = gPO(Class);
return sPO(Class, sPO(function Super() {
return construct$$1(Parent, arguments, gPO(this).constructor);
}, Parent));
};
}(Object);
These two lines in particular confuse me:
Constructor = Parent.bind.apply(Parent, a);
return sPO(new Constructor(), Class.prototype);
I think Parent.bind.apply(Parent, a) ends up producing Constructor where calling new Constructor() results in this inside the constructor being an instance of Constructor, and one which Object.getPrototypeOf(this) is Parent.prototype. The custom elements polyfill uses this.constructor in place of new.target, so this prevents it from determining what constructor you've called and thus finding the custom element definition for that constructor.
The part I find most confusing about those lines is that modifying it like this:
Constructor = Parent.bind.apply(Parent, a);
Constructor.prototype = Object.create(Parent.prototype); // new
Constructor.prototype.constructor = Class; // new
return sPO(new Constructor(), Class.prototype);
doesn't actually change the prototype of the object that becomes this inside new Constructor(). Parent.bind.apply(Parent, a) prevents this in the same way that F.bind(null) here prevents it:
const F = function() {
console.log("this ->", this);
console.log(
"Object.getPrototypeOf(this) === F.prototype ->",
Object.getPrototypeOf(this) === F.prototype
);
};
const G = F.bind(null);
G.prototype = Object.create(F.prototype);
G.prototype.constructor = G;
new G();
output:
this -> F {}
Object.getPrototypeOf(this) === F.prototype -> true
In general, I don't think this is an issue with the polyfill but rather _fixBabelExtend preventing the polyfill from finding the constructor.
(Edit: Also I bet this is only shows up in IE11 because _Reflect$construct is set to Reflect.construct in other browsers, skipping the broken section.)
Now I realize why that .bind.apply is happening: it's for the purpose of constructing with variable arguments in ES5.
I have posted a question here: https://stackoverflow.com/questions/50617479/angular-6-custom-elements-fail-on-ie11-and-firefox-with-syntax-and-shadow-dom-er because these polyfills really don't seem to be working on IE11?
https://github.com/WebReflection/document-register-element is the unique polyfill that works fine to me on IE11.
The Babel 7.0.0 beta releases have changed the way they transform ES6 classes extending built-in constructors which may only be constructed with new / Reflect.construct down to ES5 - this includes custom elements, which extend HTMLElement. Particularly, the way they've changed their Reflect.construct-like helper function causes this.constructor to point to the wrong function inside the constructor that they're calling, which prevents the polyfill from finding the right definition. AFAICT, there isn't a way to tell them not to use their custom construct function because they just check if the expression following extends is one of a set of identifiers known to be built-in constructors; the polyfill replaces HTMLElement, so it doesn't look different as far as their check is concerned.
In the Polymer tools repo, for the CLI's build and serve commands, we've pinned @babel/plugin-transform-classes to 7.0.0-beta.35 in the mean time. I'm still trying to come up with a good solution for this; as of now, sending a patch to @babel/plugin-transform-classes adding a plugin option that would let you explicitly disable their special construct helper for certain built-ins (HTMLElement here) is looking like the best bet.
@bicknellr Think this is still an issue?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically closed after being marked stale. If you're still facing this problem with the above solution, please comment and we'll reopen!