-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathresource_timing.html
More file actions
214 lines (195 loc) · 11.4 KB
/
resource_timing.html
File metadata and controls
214 lines (195 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Resource Timing API Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
}
/* Simple animation for new rows */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.5s ease-out forwards;
}
/* Style for timings that are not applicable */
.not-applicable {
opacity: 0.5;
text-decoration: line-through;
}
/* Responsive table styles */
@media (max-width: 767px) { /* Tailwind's md breakpoint is 768px */
.responsive-table thead {
display: none;
}
.responsive-table tbody, .responsive-table tr {
display: block;
}
.responsive-table tr {
margin-bottom: 1.5rem;
border: 1px solid #e5e7eb; /* border-gray-200 */
border-radius: 0.75rem; /* rounded-xl */
overflow: hidden;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); /* shadow-md */
}
.responsive-table td {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem; /* py-3 px-4 */
border-bottom: 1px solid #e5e7eb; /* border-gray-200 */
text-align: right;
}
.responsive-table tr:last-child td:last-child {
border-bottom: 0;
}
.responsive-table td[data-label]::before {
content: attr(data-label);
font-weight: 500; /* font-medium */
color: #4b5563; /* text-gray-600 */
text-align: left;
}
.responsive-table td:first-child {
background-color: #f9fafb; /* bg-gray-50 */
font-weight: 500; /* font-medium */
padding: 1rem; /* p-4 */
flex-direction: column;
align-items: flex-start;
word-break: break-all;
}
.responsive-table td:first-child::before {
display: none;
}
}
</style>
</head>
<body class="bg-gray-50 text-gray-800">
<div class="container mx-auto p-4 md:p-8">
<header class="text-center mb-8">
<h1 class="text-3xl md:text-4xl font-bold text-gray-900">Resource Timing API Demo</h1>
<p class="mt-2 text-lg text-gray-600">Observing network timing for page resources.</p>
<p class="mt-2 text-lg text-gray-600"><a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Resource_timing">Documentation</a></p>
</header>
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200">
<!-- Table to display results -->
<div class="overflow-x-auto">
<table class="responsive-table min-w-full bg-white border border-gray-200 rounded-lg">
<thead class="bg-gray-100">
<tr>
<th class="text-left py-3 px-4 font-semibold text-gray-600">Resource URL</th>
<th class="text-right py-3 px-4 font-semibold text-gray-600" title="Domain Name System lookup time">DNS (ms)</th>
<th class="text-right py-3 px-4 font-semibold text-gray-600" title="TCP connection time">TCP (ms)</th>
<th class="text-right py-3 px-4 font-semibold text-gray-600" title="Time to establish a secure connection">TLS (ms)</th>
<th class="text-right py-3 px-4 font-semibold text-gray-600" title="Time from sending request to first byte of response">Request (TTFB)</th>
<th class="text-right py-3 px-4 font-semibold text-gray-600" title="Time to download the resource content">Download (ms)</th>
<th class="text-right py-3 px-4 font-semibold text-gray-600" title="Total time from start to finish">Total (ms)</th>
</tr>
</thead>
<tbody id="timingTableBody">
<!-- Timing data will be inserted here by JavaScript -->
</tbody>
</table>
<div id="no-observer-message" class="hidden text-center p-4 bg-yellow-100 text-yellow-800 rounded-lg mt-4">
Your browser does not support the PerformanceObserver API. Please try a modern browser.
</div>
</div>
</div>
<footer class="text-center mt-8 text-gray-500 text-sm">
<p>Reload the page to clear results. Timings are in milliseconds (ms).</p>
</footer>
</div>
<script>
function bothZeroOrUndefined(a, b) {
return ((a === undefined) || (a === 0.0)) && ((b === undefined) || (b === 0.0));
}
// Check if the PerformanceObserver API is supported
if ('PerformanceObserver' in window) {
const timingTableBody = document.getElementById('timingTableBody');
// Function to format timing data for display
const formatTiming = (start, end) => {
if (start === undefined || end === undefined || (start === 0 && end === 0)) {
return `n/a`;
}
const duration = (end - start).toFixed(2);
return `${start.toFixed(2)} to ${end.toFixed(2)}<br />${duration}ms`;
};
// Function to process performance entries and update the table
const processEntries = (entries) => {
entries.forEach(entry => {
// We are interested in 'resource' and 'navigation' type entries
if (entry.entryType === 'resource' || entry.entryType === 'navigation') {
// Create a new row for the table
const newRow = document.createElement('tr');
newRow.className = 'border-t border-gray-200 fade-in';
if (entry.entryType === 'navigation') {
// Add a subtle background to highlight the main page load
newRow.classList.add('bg-blue-50');
}
// --- Calculate timing metrics ---
const dnsTime = formatTiming(entry.domainLookupStart, entry.domainLookupEnd);
const tcpTime = formatTiming(entry.connectStart, entry.secureConnectionStart);
const tlsTime = formatTiming(entry.secureConnectionStart, entry.connectEnd);
const requestTime = formatTiming(entry.requestStart, entry.responseStart);
const downloadTime = formatTiming(entry.responseStart, entry.responseEnd);
const totalTime = formatTiming(entry.startTime, entry.responseEnd);
// --- Determine if a stage is not applicable (duration is zero) ---
// This can happen if the resource is cached, a connection is reused, or the stage doesn't apply (e.g. TLS for HTTP).
// const dnsNotApplicable = (entry.domainLookupStart === undefined) || (entry.domainLookupStart === 0.0);
// const tcpNotApplicable = (entry.connectStart === undefined) || (entry.connectStart === 0.0);
// const tlsNotApplicable = (entry.secureConnectionStart === undefined) || (entry.secureConnectionStart === 0.0);
// const requestNotApplicable = (entry.requestStart === undefined) || (entry.requestStart === 0.0);
const dnsNotApplicable = bothZeroOrUndefined(entry.domainLookupStart, entry.domainLookupEnd);
const tcpNotApplicable = bothZeroOrUndefined(entry.connectStart, entry.secureConnectionStart);
const tlsNotApplicable = bothZeroOrUndefined(entry.secureConnectionStart, entry.connectEnd);
const requestNotApplicable = bothZeroOrUndefined(entry.requestStart, entry.responseStart);
// Use a specific name for the navigation entry, otherwise truncate the URL
const url = entry.entryType === 'navigation'
? 'This Page (Document)'
: entry.name.length > 50 ? `${entry.name.substring(0, 50)}...` : entry.name;
const fullUrl = entry.name;
// Populate the row with data
newRow.innerHTML = `
<td class="py-3 px-4 text-sm text-gray-700 font-medium" title="${fullUrl}">${url}</td>
<td class="py-3 px-4 text-right text-sm text-gray-700 ${dnsNotApplicable ? 'not-applicable' : ''}" data-label="DNS (ms)">${dnsTime}</td>
<td class="py-3 px-4 text-right text-sm text-gray-700 ${tcpNotApplicable ? 'not-applicable' : ''}" data-label="TCP (ms)">${tcpTime}</td>
<td class="py-3 px-4 text-right text-sm text-gray-700 ${tlsNotApplicable ? 'not-applicable' : ''}" data-label="TLS (ms)">${tlsTime}</td>
<td class="py-3 px-4 text-right text-sm text-gray-700 ${requestNotApplicable ? 'not-applicable' : ''}" data-label="Request (TTFB)">${requestTime}</td>
<td class="py-3 px-4 text-right text-sm text-gray-700" data-label="Download (ms)">${downloadTime}</td>
<td class="py-3 px-4 text-right text-sm font-medium text-gray-800" data-label="Total (ms)">${totalTime}</td>
`;
// Add the new row to the table. Prepend navigation entry to show it at the top.
if (entry.entryType === 'navigation') {
timingTableBody.prepend(newRow);
} else {
timingTableBody.appendChild(newRow);
}
}
});
};
// Create a PerformanceObserver to watch for resource loading
const observer = new PerformanceObserver((list) => {
processEntries(list.getEntries());
});
// Start observing.
// 'buffered: true' ensures we get entries for resources that loaded before the observer was created.
observer.observe({ type: 'resource', buffered: true });
// Get the navigation timing for the page itself on load
window.addEventListener('load', () => {
const navEntries = performance.getEntriesByType('navigation');
if (navEntries.length > 0) {
processEntries(navEntries);
}
});
} else {
// Show a message if the browser doesn't support the API
document.getElementById('no-observer-message').classList.remove('hidden');
}
</script>
</body>
</html>