Skip to content

Commit bfebb36

Browse files
committed
perf: speed up focus.set by ~22x
1 parent 6c10c13 commit bfebb36

File tree

1 file changed

+66
-32
lines changed

1 file changed

+66
-32
lines changed

frontend/src/ts/test/focus.ts

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,80 @@ import * as PageTransition from "../states/page-transition";
88
const unfocusPx = 3;
99
let state = false;
1010

11-
export function set(foc: boolean, withCursor = false): void {
12-
if (foc && !state) {
11+
let cacheReady = false;
12+
let cache: {
13+
focus?: HTMLElement[];
14+
cursor?: HTMLElement[];
15+
} = {};
16+
17+
function initializeCache(): void {
18+
if (cacheReady) return;
19+
20+
const cursorSelector = "body, button, a";
21+
const elementsSelector = [
22+
"app",
23+
"header",
24+
"footer",
25+
"main",
26+
"#bannerCenter",
27+
"#notificationCenter",
28+
"#capsWarning",
29+
"#ad-vertical-right-wrapper",
30+
"#ad-vertical-left-wrapper",
31+
"#ad-footer-wrapper",
32+
"#ad-footer-small-wrapper",
33+
].join(",");
34+
35+
cache.cursor = [...document.querySelectorAll<HTMLElement>(cursorSelector)];
36+
cache.focus = [...document.querySelectorAll<HTMLElement>(elementsSelector)];
37+
38+
cacheReady = true;
39+
}
40+
41+
// with cursor is a special case that is only used on the initial page load
42+
// to avoid the cursor being invisible and confusing the user
43+
export function set(value: boolean, withCursor = false): void {
44+
initializeCache();
45+
46+
if (value && !state) {
1347
state = true;
48+
49+
// batch DOM operations for better performance
50+
requestAnimationFrame(() => {
51+
if (cache.focus) {
52+
for (const el of cache.focus) {
53+
el.classList.add("focus");
54+
}
55+
}
56+
if (!withCursor && cache.cursor) {
57+
for (const el of cache.cursor) {
58+
el.style.cursor = "none";
59+
}
60+
}
61+
});
62+
1463
Caret.stopAnimation();
15-
$("header").addClass("focus");
16-
$("footer").addClass("focus");
17-
if (!withCursor) {
18-
$("body").css("cursor", "none");
19-
$("button").css("cursor", "none");
20-
$("a").css("cursor", "none");
21-
}
22-
$("main").addClass("focus");
23-
$("#bannerCenter").addClass("focus");
24-
$("#notificationCenter").addClass("focus");
25-
$("#capsWarning").addClass("focus");
26-
$("#ad-vertical-right-wrapper").addClass("focus");
27-
$("#ad-vertical-left-wrapper").addClass("focus");
28-
$("#ad-footer-wrapper").addClass("focus");
29-
$("#ad-footer-small-wrapper").addClass("focus");
3064
LiveSpeed.show();
3165
LiveBurst.show();
3266
LiveAcc.show();
3367
TimerProgress.show();
34-
} else if (!foc && state) {
68+
} else if (!value && state) {
3569
state = false;
70+
71+
requestAnimationFrame(() => {
72+
if (cache.focus) {
73+
for (const el of cache.focus) {
74+
el.classList.remove("focus");
75+
}
76+
}
77+
if (cache.cursor) {
78+
for (const el of cache.cursor) {
79+
el.style.cursor = "";
80+
}
81+
}
82+
});
83+
3684
Caret.startAnimation();
37-
$("header").removeClass("focus");
38-
$("footer").removeClass("focus");
39-
$("body").css("cursor", "");
40-
$("button").css("cursor", "");
41-
$("a").css("cursor", "");
42-
$("main").removeClass("focus");
43-
$("#bannerCenter").removeClass("focus");
44-
$("#notificationCenter").removeClass("focus");
45-
$("#capsWarning").removeClass("focus");
46-
$("#app").removeClass("focus");
47-
$("#ad-vertical-right-wrapper").removeClass("focus");
48-
$("#ad-vertical-left-wrapper").removeClass("focus");
49-
$("#ad-footer-wrapper").removeClass("focus");
50-
$("#ad-footer-small-wrapper").removeClass("focus");
5185
LiveSpeed.hide();
5286
LiveBurst.hide();
5387
LiveAcc.hide();

0 commit comments

Comments
 (0)