mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
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.
This commit is contained in:
parent
80ed94ed08
commit
e92d7f13e3
3 changed files with 59 additions and 1 deletions
|
@ -172,8 +172,13 @@ def _handle_local_file_request(request: LocalFileRequest) -> Response:
|
|||
try:
|
||||
mimetype = _mime_for_path(fullpath)
|
||||
if os.path.exists(fullpath):
|
||||
if fullpath.endswith(".css"):
|
||||
# make changes to css files immediately reflected in the webview
|
||||
max_age = 10
|
||||
else:
|
||||
max_age = 60 * 60
|
||||
return flask.send_file(
|
||||
fullpath, mimetype=mimetype, conditional=True, max_age=60 * 60 # type: ignore[call-arg]
|
||||
fullpath, mimetype=mimetype, conditional=True, max_age=max_age # type: ignore[call-arg]
|
||||
)
|
||||
else:
|
||||
print(f"Not found: {path}")
|
||||
|
|
49
ts/reviewer/css.ts
Normal file
49
ts/reviewer/css.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
// 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);
|
||||
});
|
||||
}
|
|
@ -21,6 +21,7 @@ globalThis.anki.mutateNextCardStates = mutateNextCardStates;
|
|||
|
||||
import { bridgeCommand } from "../lib/bridgecommand";
|
||||
import { allImagesLoaded, preloadAnswerImages } from "./images";
|
||||
import { maybePreloadExternalCss } from "./css";
|
||||
declare const MathJax: any;
|
||||
|
||||
type Callback = () => void | Promise<void>;
|
||||
|
@ -113,6 +114,9 @@ export async function _updateQA(
|
|||
|
||||
const qa = document.getElementById("qa")!;
|
||||
|
||||
// prevent flash of unstyled content when external css used
|
||||
await maybePreloadExternalCss(html);
|
||||
|
||||
qa.style.opacity = "0";
|
||||
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue