Skip to content

Commit

Permalink
fix(IE11, docs): Added potential fix for #24 and #23
Browse files Browse the repository at this point in the history
  • Loading branch information
calebdwilliams committed Aug 30, 2019
1 parent 1b81f79 commit 0bf415e
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 61 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ This polyfill will create a new style element for every `DocumentOrShadowRoot` i

No changes will occur in a browser that supports the feature by default.

## Requirements

This polyfill should work as-is in modern browsers, however, to support IE11, you will also need to consume a polyfill for [ECMAScript `Symbol`s](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). One popular and well supported option is [Core-JS](https://www.npmjs.com/package/core-js) which includes polyfills for many other modern features alongside a polyfill for `Symbol`.

## Installation

The package can be installed either by copying the [polyfill file](./adoptedStyleSheets.js) or by [installing using npm](https://docs.npmjs.com/getting-started/).
Expand Down
128 changes: 67 additions & 61 deletions adoptedStyleSheets.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
// Move style elements created before document.body
// to the iframe along with future styles
deferredStyleSheets.forEach(nativeStyleSheet => {
frameBody.append(nativeStyleSheet);
frameBody.appendChild(nativeStyleSheet);
nativeStyleSheet.disabled = false;
});

Expand Down Expand Up @@ -112,78 +112,79 @@
// cannot be instantiated. The `new` operation will return the native
// CSSStyleSheet object extracted from a style element appended to the
// iframe.
class ConstructStyleSheet {
// Allows instanceof checks with the window.CSSStyleSheet.
static [Symbol.hasInstance](instance) {
return instance instanceof OldCSSStyleSheet;
function ConstructStyleSheet() {
// A style element to extract the native CSSStyleSheet object.
const basicStyleElement = document.createElement('style');

if (polyfillLoaded) {
// If the polyfill is ready, use the framebody
frameBody.appendChild(basicStyleElement);
} else {
// If the polyfill is not ready, move styles to head temporarily
document.head.appendChild(basicStyleElement);
basicStyleElement.disabled = true;
deferredStyleSheets.push(basicStyleElement);
}

constructor() {
// A style element to extract the native CSSStyleSheet object.
const basicStyleElement = document.createElement('style');
const nativeStyleSheet = basicStyleElement.sheet;

if (polyfillLoaded) {
// If the polyfill is ready, use the framebody
frameBody.append(basicStyleElement);
} else {
// If the polyfill is not ready, move styles to head temporarily
document.head.append(basicStyleElement);
basicStyleElement.disabled = true;
deferredStyleSheets.push(basicStyleElement);
}

const nativeStyleSheet = basicStyleElement.sheet;

// A support object to preserve all the polyfill data
constructStyleSheetRegistry.set(nativeStyleSheet, {
adopters: new Map(),
actions: [],
basicStyleElement,
});

return nativeStyleSheet;
}
// A support object to preserve all the polyfill data
constructStyleSheetRegistry.set(nativeStyleSheet, {
adopters: new Map(),
actions: [],
basicStyleElement,
});

replace(contents) {
return new Promise((resolve, reject) => {
if (constructStyleSheetRegistry.has(this)) {
const {basicStyleElement} = constructStyleSheetRegistry.get(this);

basicStyleElement.innerHTML = contents;
resolve(basicStyleElement.sheet);
updateAdopters(this);
} else {
reject(
new DOMException(
"Failed to execute 'replace' on 'CSSStyleSheet': Can't call replace on non-constructed CSSStyleSheets.",
'NotAllowedError',
),
);
}
});
}
return nativeStyleSheet;
}

replaceSync(contents) {
if (importPattern.test(contents)) {
throw new DOMException(
'@import rules are not allowed when creating stylesheet synchronously',
'NotAllowedError',
);
Object.defineProperty(ConstructStyleSheet, Symbol.hasInstance, {
get() {
return function(instance) {
return instance instanceof OldCSSStyleSheet;
}
}
});

ConstructStyleSheet.prototype.replace = function(contents) {
return new Promise((resolve, reject) => {
if (constructStyleSheetRegistry.has(this)) {
const {basicStyleElement} = constructStyleSheetRegistry.get(this);

basicStyleElement.innerHTML = contents;
resolve(basicStyleElement.sheet);
updateAdopters(this);

return basicStyleElement.sheet;
} else {
throw new DOMException(
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets.",
'NotAllowedError',
reject(
new DOMException(
"Failed to execute 'replace' on 'CSSStyleSheet': Can't call replace on non-constructed CSSStyleSheets.",
'NotAllowedError',
),
);
}
});
}

ConstructStyleSheet.prototype.replaceSync = function(contents) {
if (importPattern.test(contents)) {
throw new DOMException(
'@import rules are not allowed when creating stylesheet synchronously',
'NotAllowedError',
);
}

if (constructStyleSheetRegistry.has(this)) {
const {basicStyleElement} = constructStyleSheetRegistry.get(this);

basicStyleElement.innerHTML = contents;
updateAdopters(this);

return basicStyleElement.sheet;
} else {
throw new DOMException(
"Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets.",
'NotAllowedError',
);
}
}

Expand All @@ -208,7 +209,7 @@
const observer = observerRegistry.get(location);

observer.disconnect();
newStyles.append(adoptedStyleElement);
newStyles.appendChild(adoptedStyleElement);
observer.observe();
} else {
const clone = basicStyleElement.cloneNode(true);
Expand All @@ -217,13 +218,18 @@
// element (e.g., it was disconnected).
appliedActionsCursorRegistry.set(clone, 0);
adopters.set(location, clone);
newStyles.append(clone);
newStyles.appendChild(clone);
}
}

// Since we already removed all elements during appending them to the
// document fragment, we can just re-add them again.
location.prepend(newStyles);
// we do this because IE11 doesn't support ParentNode.prepend
if (location.firstChild) {
location.insertBefore(newStyles, location.firstChild);
} else {
location.appendChild(newStyles);
}

// We need to apply all actions we have done with the original CSSStyleSheet
// to each new style element and to any other element that missed last
Expand Down

0 comments on commit 0bf415e

Please sign in to comment.