|
1 | | -// CLS Quick Check |
2 | | -// https://webperf-snippets.nucliweb.net |
3 | | - |
4 | 1 | (() => { |
5 | 2 | let cls = 0; |
6 | 3 |
|
7 | 4 | const valueToRating = (score) => |
8 | 5 | score <= 0.1 ? "good" : score <= 0.25 ? "needs-improvement" : "poor"; |
9 | 6 |
|
10 | | - const RATING = { |
11 | | - good: { icon: "🟢", color: "#0CCE6A" }, |
12 | | - "needs-improvement": { icon: "🟡", color: "#FFA400" }, |
13 | | - poor: { icon: "🔴", color: "#FF4E42" }, |
14 | | - }; |
15 | | - |
16 | | - const logCLS = () => { |
17 | | - const rating = valueToRating(cls); |
18 | | - const { icon, color } = RATING[rating]; |
19 | | - console.log( |
20 | | - `%cCLS: ${icon} ${cls.toFixed(4)} (${rating})`, |
21 | | - `color: ${color}; font-weight: bold; font-size: 14px;` |
22 | | - ); |
23 | | - }; |
24 | | - |
25 | 7 | const observer = new PerformanceObserver((list) => { |
26 | 8 | for (const entry of list.getEntries()) { |
27 | | - if (!entry.hadRecentInput) { |
28 | | - cls += entry.value; |
29 | | - } |
| 9 | + if (!entry.hadRecentInput) cls += entry.value; |
30 | 10 | } |
31 | | - logCLS(); |
32 | 11 | }); |
33 | 12 |
|
34 | 13 | observer.observe({ type: "layout-shift", buffered: true }); |
35 | 14 |
|
36 | | - // Update on visibility change (final CLS) |
37 | 15 | document.addEventListener("visibilitychange", () => { |
38 | | - if (document.visibilityState === "hidden") { |
39 | | - observer.takeRecords(); |
40 | | - console.log("%c📊 Final CLS (on page hide):", "font-weight: bold;"); |
41 | | - logCLS(); |
42 | | - } |
| 16 | + if (document.visibilityState === "hidden") observer.takeRecords(); |
43 | 17 | }); |
44 | 18 |
|
45 | | - // Expose function for manual check |
46 | | - window.getCLS = () => { |
47 | | - logCLS(); |
48 | | - const rating = valueToRating(cls); |
49 | | - return { |
50 | | - script: "CLS", |
51 | | - status: "ok", |
52 | | - metric: "CLS", |
53 | | - value: Math.round(cls * 10000) / 10000, |
54 | | - unit: "score", |
55 | | - rating, |
56 | | - thresholds: { good: 0.1, needsImprovement: 0.25 }, |
57 | | - }; |
58 | | - }; |
59 | | - |
60 | | - console.log( |
61 | | - " Call %cgetCLS()%c anytime to check current value.", |
62 | | - "font-family: monospace; background: #f3f4f6; padding: 2px 4px;", |
63 | | - "" |
64 | | - ); |
| 19 | + window.getCLS = () => ({ |
| 20 | + script: "CLS", |
| 21 | + status: "ok", |
| 22 | + metric: "CLS", |
| 23 | + value: Math.round(cls * 10000) / 10000, |
| 24 | + unit: "score", |
| 25 | + rating: valueToRating(cls), |
| 26 | + thresholds: { good: 0.1, needsImprovement: 0.25 }, |
| 27 | + }); |
65 | 28 |
|
66 | 29 | // Synchronous return for agent (buffered entries) |
67 | | - const clsSync = performance.getEntriesByType("layout-shift") |
68 | | - .reduce((sum, e) => !e.hadRecentInput ? sum + e.value : sum, 0); |
69 | | - const clsRating = valueToRating(clsSync); |
| 30 | + const clsSync = performance |
| 31 | + .getEntriesByType("layout-shift") |
| 32 | + .reduce((sum, e) => (!e.hadRecentInput ? sum + e.value : sum), 0); |
| 33 | + |
70 | 34 | return { |
71 | 35 | script: "CLS", |
72 | 36 | status: "ok", |
73 | 37 | metric: "CLS", |
74 | 38 | value: Math.round(clsSync * 10000) / 10000, |
75 | 39 | unit: "score", |
76 | | - rating: clsRating, |
| 40 | + rating: valueToRating(clsSync), |
77 | 41 | thresholds: { good: 0.1, needsImprovement: 0.25 }, |
78 | 42 | }; |
79 | 43 | })(); |
0 commit comments