anki/ts/reviewer/css.ts
Hikaru Y e92d7f13e3
Preload external css files to prevent flash of unstyled content (#1558)
* Preload external css files to prevent flash of unstyled content

This is an implementation of the approach mentioned in the commit
message of 46b85d5.

* Tweak max_age value for css files

Ensure that css preloading works even on a slow PC.
2021-12-16 21:47:10 +10:00

50 lines
1.6 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
type CssElementType = HTMLStyleElement | HTMLLinkElement;
const preloadCssClassName = "preload-css";
const template = document.createElement("template");
export async function maybePreloadExternalCss(html: string): Promise<void> {
clearPreloadedCss();
template.innerHTML = html;
const externalCssElements = extractExternalCssElements(template.content);
if (externalCssElements.length) {
await Promise.race([
Promise.all(externalCssElements.map(injectAndLoadCss)),
new Promise((r) => setTimeout(r, 500)),
]);
}
}
function clearPreloadedCss(): void {
[...document.head.getElementsByClassName(preloadCssClassName)].forEach((css) =>
css.remove(),
);
}
function extractExternalCssElements(fragment: DocumentFragment): CssElementType[] {
return <CssElementType[]>(
[...fragment.querySelectorAll("style, link")].filter(
(css) =>
(css instanceof HTMLStyleElement &&
css.innerHTML.includes("@import")) ||
(css instanceof HTMLLinkElement && css.rel === "stylesheet"),
)
);
}
function injectAndLoadCss(css: CssElementType): Promise<void> {
return new Promise((resolve) => {
css.classList.add(preloadCssClassName);
// this prevents the css from affecting the page rendering
css.media = "print";
css.addEventListener("load", () => resolve());
css.addEventListener("error", () => resolve());
document.head.appendChild(css);
});
}