Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
1 of 6 tasks
trusktr opened this issue Jan 12, 2018 · 10 comments

Comments

@trusktr
Copy link

trusktr commented Jan 12, 2018

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
  • IE 11

Versions

@trusktr
Copy link
Author

trusktr commented Jan 12, 2018

The other error that also appears in the console is reported in webcomponents/shadydom#207.

@TimvdLippe
Copy link
Contributor

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

@rzymek01
Copy link

+1
The same here.

Simple use case:

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

@bicknellr
Copy link
Collaborator

bicknellr commented May 22, 2018

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
Copy link
Collaborator

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

@jimmymain
Copy link

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?

@007lva
Copy link

007lva commented Jul 10, 2018

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

@bicknellr
Copy link
Collaborator

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.

@dfreedm dfreedm transferred this issue from webcomponents/custom-elements Jun 7, 2019
dfreedm pushed a commit that referenced this issue Jun 11, 2019
@dfreedm dfreedm changed the title Error in IE11: The custom element being constructed was not registered with customElements. [Custom Elements] Error in IE11: The custom element being constructed was not registered with customElements. Jun 12, 2019
@dfreedm
Copy link
Contributor

dfreedm commented Jun 12, 2019

@bicknellr Think this is still an issue?

@stale
Copy link

stale bot commented Apr 24, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants