polyfills icon indicating copy to clipboard operation
polyfills copied to clipboard

[Custom Elements] Error in IE11: The custom element being constructed was not registered with `customElements`.

Open trusktr opened this issue 8 years ago • 11 comments

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

trusktr avatar Jan 12 '18 06:01 trusktr

The other error that also appears in the console is reported in https://github.com/webcomponents/shadydom/issues/207.

trusktr avatar Jan 12 '18 06:01 trusktr

The pen is quite big. Could you provide a minimal reproduction case that shows the issue? That eases the debugging process.

TimvdLippe avatar Mar 31 '18 22:03 TimvdLippe

+1 The same here.

Simple use case:

  • including custom-elements.js polyfill
  • defining custom element per class extends HTMLElement { and customElements.define
  • using it

rzymek01 avatar Apr 11 '18 01:04 rzymek01

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.)

bicknellr avatar May 22 '18 01:05 bicknellr

Now I realize why that .bind.apply is happening: it's for the purpose of constructing with variable arguments in ES5.

bicknellr avatar May 22 '18 18:05 bicknellr

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?

jimmymain avatar Jun 01 '18 04:06 jimmymain

https://github.com/WebReflection/document-register-element is the unique polyfill that works fine to me on IE11.

007lva avatar Jul 10 '18 18:07 007lva

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 avatar Jul 10 '18 19:07 bicknellr

@bicknellr Think this is still an issue?

dfreedm avatar Jun 12 '19 19:06 dfreedm

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.

stale[bot] avatar Apr 24 '21 06:04 stale[bot]

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!

stale[bot] avatar Apr 27 '25 05:04 stale[bot]