Skip to content

Commit 6721e60

Browse files
committed
feat: defer page timing caching until load event and add global metrics
1 parent 27f1588 commit 6721e60

3 files changed

Lines changed: 41 additions & 93 deletions

File tree

src/view/frontend/web/js/inspector.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,14 @@ function _registerMageforgeInspector() {
9696
this.createHighlightBox();
9797
this.createInfoBadge();
9898
this.initWebVitalsTracking();
99-
this.cachePageTimings();
99+
100+
// Defer page timings until the load event so that loadEventEnd and
101+
// domContentLoadedEventEnd are actually populated by the browser.
102+
if (document.readyState === 'complete') {
103+
setTimeout(() => this.cachePageTimings(), 0);
104+
} else {
105+
window.addEventListener('load', () => setTimeout(() => this.cachePageTimings(), 0), { once: true });
106+
}
100107

101108
// Listen for inspector-state sync from toolbar
102109
this._inspectorStateHandler = (e) => {

src/view/frontend/web/js/inspector/performance.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,37 @@ export const performanceMethods = {
114114
if (!hasMetrics) {
115115
this.renderNoBrowserMetrics(container);
116116
}
117+
118+
this.renderPageTimingsSection(container);
119+
},
120+
121+
/**
122+
* Render page-level timing metrics (not element-specific)
123+
*
124+
* @param {HTMLElement} container
125+
*/
126+
renderPageTimingsSection(container) {
127+
// Re-read if not yet cached or still 0 (init ran before load event)
128+
if (!this.pageTimings || this.pageTimings.domContentLoaded === 0) {
129+
this.cachePageTimings();
130+
}
131+
132+
if (!this.pageTimings || this.pageTimings.domContentLoaded === 0) {
133+
return;
134+
}
135+
136+
const divider = document.createElement('div');
137+
divider.style.cssText = 'border-top: 1px solid rgba(255,255,255,0.1); margin: 12px 0 8px; padding-top: 8px; font-size: 10px; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em;';
138+
divider.textContent = 'Global Page-Metrics';
139+
container.appendChild(divider);
140+
141+
const dclColor = this.pageTimings.domContentLoaded < 1500 ? '#34d399' : (this.pageTimings.domContentLoaded < 3000 ? '#f59e0b' : '#ef4444');
142+
container.appendChild(this.createInfoSection('DOM Content Loaded', `${this.pageTimings.domContentLoaded} ms`, dclColor));
143+
144+
if (this.pageTimings.loadComplete > 0) {
145+
const loadColor = this.pageTimings.loadComplete < 2500 ? '#34d399' : (this.pageTimings.loadComplete < 5000 ? '#f59e0b' : '#ef4444');
146+
container.appendChild(this.createInfoSection('Page Load Complete', `${this.pageTimings.loadComplete} ms`, loadColor));
147+
}
117148
},
118149

119150
renderRenderTimeMetric(container, element) {

src/view/frontend/web/js/inspector/vitals.js

Lines changed: 2 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export const vitalsMethods = {
9797
if (navEntries && navEntries.length > 0) {
9898
const nav = navEntries[0];
9999
this.pageTimings = {
100-
domContentLoaded: Math.round(nav.domContentLoadedEventEnd - nav.domContentLoadedEventStart),
100+
domContentLoaded: Math.round(nav.domContentLoadedEventEnd - nav.startTime),
101101
loadComplete: Math.round(nav.loadEventEnd - nav.fetchStart)
102102
};
103103
} else if (performance.timing) {
@@ -125,7 +125,7 @@ export const vitalsMethods = {
125125
this.webVitals.cls.forEach(shift => {
126126
if (shift.sources) {
127127
shift.sources.forEach(source => {
128-
if (source.node === element || element.contains(source.node) || source.node.contains(element)) {
128+
if (source.node && (source.node === element || element.contains(source.node) || source.node.contains(element))) {
129129
totalCLS += shift.value;
130130
}
131131
});
@@ -224,96 +224,6 @@ export const vitalsMethods = {
224224
return result;
225225
},
226226

227-
/**
228-
* Calculate DOM complexity metrics
229-
*
230-
* @param {HTMLElement} element - The element to analyze
231-
* @return {{childCount: number, depth: number, totalNodes: number}}
232-
*/
233-
calculateDOMComplexity(element) {
234-
if (!element || !(element instanceof HTMLElement)) {
235-
return { childCount: 0, depth: 0, totalNodes: 0 };
236-
}
237-
238-
const childCount = element.childElementCount;
239-
const totalNodes = element.querySelectorAll('*').length;
240-
const depth = this.getMaxDepth(element);
241-
242-
return { childCount, depth, totalNodes };
243-
},
244-
245-
/**
246-
* Get maximum depth of element tree
247-
*
248-
* @param {HTMLElement} element
249-
* @param {number} currentDepth
250-
* @return {number}
251-
*/
252-
getMaxDepth(element, currentDepth = 0) {
253-
if (!element.children.length) {
254-
return currentDepth;
255-
}
256-
257-
let maxChildDepth = currentDepth;
258-
for (const child of element.children) {
259-
const depth = this.getMaxDepth(child, currentDepth + 1);
260-
maxChildDepth = Math.max(maxChildDepth, depth);
261-
}
262-
263-
return maxChildDepth;
264-
},
265-
266-
/**
267-
* Get complexity rating based on total nodes
268-
*
269-
* @param {{childCount: number, depth: number, totalNodes: number}} complexity
270-
* @return {string} 'low' | 'medium' | 'high'
271-
*/
272-
getComplexityRating(complexity) {
273-
if (complexity.totalNodes < this.PERF_DOM_COMPLEXITY_LOW) {
274-
return 'low';
275-
} else if (complexity.totalNodes < this.PERF_DOM_COMPLEXITY_HIGH) {
276-
return 'medium';
277-
} else {
278-
return 'high';
279-
}
280-
},
281-
282-
/**
283-
* Get Web Vitals information for specific element
284-
*
285-
* @param {HTMLElement} element
286-
* @return {{isLCP: boolean, contributesCLS: number, isInteractive: boolean}}
287-
*/
288-
getWebVitalsForElement(element) {
289-
const result = {
290-
isLCP: false,
291-
contributesCLS: 0,
292-
isInteractive: false
293-
};
294-
295-
// Check if element is LCP candidate
296-
if (this.webVitals.lcp && this.webVitals.lcp.element) {
297-
result.isLCP = this.webVitals.lcp.element === element ||
298-
element.contains(this.webVitals.lcp.element);
299-
}
300-
301-
// Calculate CLS contribution
302-
if (this.webVitals.cls && this.webVitals.cls.length > 0) {
303-
this.webVitals.cls.forEach(shift => {
304-
if (shift.sources) {
305-
shift.sources.forEach(source => {
306-
if (source.node === element || element.contains(source.node)) {
307-
result.contributesCLS += shift.value;
308-
}
309-
});
310-
}
311-
});
312-
}
313-
314-
return result;
315-
},
316-
317227
/**
318228
* Get color for render time based on thresholds
319229
*

0 commit comments

Comments
 (0)