How Polyfills Remove Browser Compatibility

Samuel Gómez
Valtech Switzerland
4 min readOct 7, 2019
Some things work well separately, but not together

Tough but interesting experience collected during the last month: we had a piece of frontend (a web component) that was working fine on IE11 and Chrome when isolated. However, it was not working once integrated in an existing client application. Why:

💊 POLYFILLS 💊

In short: Polyfills shipped with our web component conflicted with polyfills of the existing client application.

What happened

A polyfill is a shim for a browser API, that implements a feature on web browsers that do not support the feature (Wikipedia Definition).

On IE11

On IE11 both our polyfills and the existing ones were trying to create “window.CustomElementRegistry”, and only the one doing it first was succeeding. The second was throwing this error:

SCRIPT5045: Assignment to read-only properties is not allowed in strict mode

Here’s a embedded proof of concept loading polyfills that Stencil 0.15 and Stencil 1.x load: document-register-element and Polymer.

On Chrome

On Chrome one of the polyfills overwrote “window.customElements.define”, thus making another one think the browser is not modern, and thus invoking a non-standard API. This was throwing the error:

Uncaught TypeError: Failed to construct ‘HTMLElement’: Please use the ‘new’ operator, this DOM object constructor cannot be called as a function.

In particular, this happens when you load core-js/es7/reflect first and then call Stencil’s defineCustomElement().

I also wanted to provide an embedded demo of this issue, but that was becoming a challenge in itself. So please find the code as part of the following demo.

Demo code

This demo showcases both issues at once in the context of an Angular 8 application. It tries to load three web components (“1”, “2” and “3”). However, “Web component 2” fails to load on IE11 and “Web component 3” fails to load on Chrome.

On IE11, “Web component 2” fails to initialize due to error “SCRIPT5045: Assignment to read-only properties is not allowed in strict mode”
On Chrome, “Web component 3” fails to initialize due to error “Uncaught TypeError: Failed to construct ‘HTMLElement’: Please use the ‘new’ operator, this DOM object constructor cannot be called as a function.”

GitHub repo is here: test-microfrontend-polyfills . Just run “npm i” and then “npm run start”.

Why it took so long to debug

  • Even though Chrome’s native definitions being overwritten was the weirdest issue, analyzing the IE11 ones was actually the hardest. Why: besides being limited in functionality, IE11 devtools were very slow and crashing every 5 minutes.
  • The compilation time of our client application was around 60 seconds, so we could not try alternatives fast.
  • Flags, flags everywhere. Only for transpilation purposes, the whole system had around half dozen flags, and we believed they could be playing a role on our compatibility issues; so we had to try them all.
  • The approach explained in this article was not even the first one. We tried first to create our web component with Angular Elements (Angular 8, no Ivy). When we realized even the simplest Proof of Concept was already too cumbersome to debug on IE11, we switched to Stencil.

Debugging tips

From the above, we can think of rather obvious tips:

  • When you can reproduce an IE11 issue on Edge, better debug it on Edge because its devtools are way better.
  • Try to keep your compilation times low, because it allows for faster trial and error, which is critical when you hit convoluted bugs.
  • If after two days you’ve still not figured out the cause, create a Proof of Concept just enough to reproduce the issue. This will allow you to iterate even faster, and at the very least realize your current approach is too problematic.

Towards a polyfill strategy

Indeed, the more polyfills you use, the higher the chances that nothing will work. Therefore you have to figure out a set of polyfills that make everybody happy. That is:

  • Together with the browser’s own supported features, they cover the API surface accessed by the whole application logic
  • They don’t overlap, or if they do, they do it in a compatible way

Now, sometimes you will be able to control which polyfills your software brings along, and sometimes not:

  • When you are in control: add polyfills from a single consistent collection (eg. a library). This way you guarantee interoperability.
  • When you are not: find out which polyfills each of the pieces is coming with, and what they are meant to polyfill. If you get two or more of them overlapping in functionality, those will be your primary suspects when debugging cross-browser issues - you will know what to start your Proofs of Concept with.

--

--