Skip to content

Commit 33ba5a3

Browse files
authored
Merge pull request #14 from safeai-aus/codex/address-performance-issues-from-review
Improve performance-safe script stability
2 parents 52ccdeb + f446768 commit 33ba5a3

File tree

1 file changed

+116
-17
lines changed

1 file changed

+116
-17
lines changed

docs/assets/performance-safe.js

Lines changed: 116 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,31 @@
33

44
(function() {
55
'use strict';
6-
6+
7+
const SAFETY_MARGIN_PX = 120;
8+
9+
function raf(callback) {
10+
if (typeof window.requestAnimationFrame === 'function') {
11+
return window.requestAnimationFrame(callback);
12+
}
13+
return window.setTimeout(callback, 16);
14+
}
15+
16+
function whenLayoutSettled(callback) {
17+
const run = function() {
18+
raf(callback);
19+
};
20+
21+
// Running twice helps ensure fonts/layout have settled
22+
raf(function() {
23+
if (document.fonts && typeof document.fonts.ready === 'object' && typeof document.fonts.ready.then === 'function') {
24+
document.fonts.ready.then(run).catch(run);
25+
} else {
26+
run();
27+
}
28+
});
29+
}
30+
731
// Safe font optimization - only adds font-display: swap to Google Fonts
832
function optimizeFonts() {
933
const fontLinks = document.querySelectorAll('link[href*="fonts.googleapis.com"]');
@@ -13,51 +37,126 @@
1337
}
1438
});
1539
}
16-
40+
41+
function markLazyImages(images) {
42+
if (!images.length) {
43+
return;
44+
}
45+
46+
whenLayoutSettled(function() {
47+
const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0;
48+
images.forEach(function(img) {
49+
if (img.hasAttribute('loading')) {
50+
return;
51+
}
52+
53+
const rect = img.getBoundingClientRect();
54+
if (rect.bottom <= 0) {
55+
return; // Above the viewport, keep eager
56+
}
57+
58+
const threshold = viewportHeight + SAFETY_MARGIN_PX;
59+
if (rect.top >= threshold) {
60+
img.loading = 'lazy';
61+
}
62+
});
63+
});
64+
}
65+
66+
function markLazyImagesWithObserver(images) {
67+
const observer = new IntersectionObserver(function(entries, obs) {
68+
entries.forEach(function(entry) {
69+
const img = entry.target;
70+
if (img.hasAttribute('loading')) {
71+
obs.unobserve(img);
72+
return;
73+
}
74+
75+
if (entry.isIntersecting) {
76+
obs.unobserve(img);
77+
return;
78+
}
79+
80+
const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0;
81+
if (entry.boundingClientRect.top >= viewportHeight + SAFETY_MARGIN_PX) {
82+
img.loading = 'lazy';
83+
obs.unobserve(img);
84+
}
85+
});
86+
}, { rootMargin: SAFETY_MARGIN_PX + 'px 0px' });
87+
88+
images.forEach(function(img) {
89+
observer.observe(img);
90+
});
91+
}
92+
1793
// Safe image optimization - only adds lazy loading to images below the fold
1894
function optimizeImages() {
19-
const images = document.querySelectorAll('img:not([loading])');
20-
images.forEach(img => {
21-
// Only add lazy loading to images that are below the fold
22-
if (img.offsetTop > window.innerHeight) {
23-
img.loading = 'lazy';
24-
}
95+
const images = Array.prototype.slice.call(document.querySelectorAll('img'));
96+
if (!images.length) {
97+
return;
98+
}
99+
100+
const candidates = images.filter(function(img) {
101+
return !img.hasAttribute('loading');
25102
});
103+
104+
if (!candidates.length) {
105+
return;
106+
}
107+
108+
if ('IntersectionObserver' in window) {
109+
markLazyImagesWithObserver(candidates);
110+
} else {
111+
markLazyImages(candidates);
112+
}
26113
}
27-
114+
28115
// Safe resource hints - only adds DNS prefetch for external domains
29116
function addResourceHints() {
30117
const externalDomains = [
31118
'fonts.googleapis.com',
32119
'fonts.gstatic.com',
33120
'cloud.umami.is'
34121
];
35-
122+
123+
const head = document.head || document.getElementsByTagName('head')[0];
124+
if (!head) {
125+
return;
126+
}
127+
128+
if (!document.querySelector('meta[http-equiv="x-dns-prefetch-control"]')) {
129+
const meta = document.createElement('meta');
130+
meta.httpEquiv = 'x-dns-prefetch-control';
131+
meta.content = 'on';
132+
head.insertBefore(meta, head.firstChild);
133+
}
134+
36135
externalDomains.forEach(domain => {
37-
if (!document.querySelector(`link[href="//${domain}"]`)) {
136+
if (!document.querySelector(`link[rel="dns-prefetch"][href="//${domain}"]`)) {
38137
const link = document.createElement('link');
39138
link.rel = 'dns-prefetch';
40139
link.href = `//${domain}`;
41-
document.head.appendChild(link);
140+
head.insertBefore(link, head.firstChild);
42141
}
43142
});
44143
}
45-
144+
46145
// Initialize only safe optimizations
47146
function init() {
48147
// Run immediately
49148
optimizeFonts();
50149
addResourceHints();
51-
150+
52151
// Run after DOM is ready
53152
if (document.readyState === 'loading') {
54-
document.addEventListener('DOMContentLoaded', optimizeImages);
153+
document.addEventListener('DOMContentLoaded', optimizeImages, { once: true });
55154
} else {
56155
optimizeImages();
57156
}
58157
}
59-
158+
60159
// Start optimizations
61160
init();
62-
161+
63162
})();

0 commit comments

Comments
 (0)