Created
September 26, 2024 19:03
-
-
Save tunetheweb/afa7f7aabde02aa3632ca4b12f380bb4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
(function () { | |
function reportWebVitals(metric, debugData) { | |
var name = metric.name, | |
delta = metric.delta, | |
id = metric.id, | |
navigationType = metric.navigationType, | |
value = metric.value, | |
entry = metric.entries[0]; | |
if (name === 'CLS') { | |
value *= 1000; | |
delta *= 1000; | |
} | |
var deviceMemory; | |
if ('deviceMemory' in navigator) { | |
deviceMemory = navigator.deviceMemory.toString(); | |
} | |
var log = { | |
event: name, | |
eventCategory: 'Web Vitals', | |
eventLabel: id, | |
eventModel: formatData(Object.assign(debugData, { | |
metric_value: value, | |
metric_delta: delta, | |
metric_id: id, | |
metric_start_time: entry && entry.startTime, | |
navigation_type: navigationType, | |
device_memory: deviceMemory | |
})) | |
}; | |
window.dataLayer = window.dataLayer || []; | |
console.log('Web Vitals', name, log.eventModel.metric_value, log); | |
window.dataLayer.push(log); | |
} | |
function reportLCP(metric) { | |
var attribution = metric.attribution; | |
var targetElement = attribution.lcpEntry && attribution.lcpEntry.element; | |
var notRestoredReasons = null; | |
var navigationEntry = attribution.navigationEntry; | |
if (navigationEntry) { | |
var activationStart = navigationEntry.activationStart || 0; | |
var redirectDuration = navigationEntry.redirectEnd - navigationEntry.redirectStart; | |
var waitingDuration = navigationEntry.workerStart || navigationEntry.fetchStart; | |
var cacheDuration = navigationEntry.domainLookupStart - waitingDuration; | |
var dnsDuration = (navigationEntry.connectStart - navigationEntry.dnsStart) || 0; | |
var connectionDuration = navigationEntry.connectEnd - navigationEntry.connectStart; | |
var serverResponseDuration = attribution.timeToFirstByte - navigationEntry.connectEnd; | |
if (navigationEntry.notRestoredReasons && navigationEntry.notRestoredReasons.reasons) { | |
notRestoredReasons = notRestoredReasons.reasons.map(function mapReasons(details) { | |
return details.reason; | |
}).join(','); | |
} | |
} | |
var debugData = { | |
debug_time_to_first_byte: attribution.timeToFirstByte, | |
debug_resource_load_delay: attribution.resourceLoadDelay, | |
debug_resource_load_duration: attribution.resourceLoadDuration, | |
debug_element_render_delay: attribution.elementRenderDelay, | |
debug_url: attribution.url, | |
debug_target: attribution.element || '(not set)', | |
debug_target_node_name: targetElement && targetElement.nodeName, | |
debug_target_loading_attr: targetElement && targetElement.getAttribute('loading'), | |
debug_nav_activation_start: activationStart, | |
debug_nav_redirect_duration: redirectDuration, | |
debug_nav_waiting_duration: waitingDuration, | |
debug_nav_cache_duration: cacheDuration, | |
debug_nav_dns_duration: dnsDuration, | |
debug_nav_connect_duration: connectionDuration, | |
debug_nav_server_response_duration: serverResponseDuration, | |
debug_not_restored_reasons: notRestoredReasons | |
}; | |
reportWebVitals(metric, debugData); | |
} | |
function reportCLS(metric) { | |
var attribution = metric.attribution; | |
var debugData = { | |
debug_time: attribution.largestShiftTime, | |
debug_load_state: attribution.loadState, | |
debug_target: attribution.largestShiftTarget || '(not set)', | |
}; | |
reportWebVitals(metric, debugData); | |
} | |
function reportINP(metric) { | |
var attribution = metric.attribution; | |
var loafData = getLoafAttribution(attribution); | |
var debugData = Object.assign({ | |
debug_event: attribution.interactionType, | |
debug_time: attribution.interactionTime, | |
debug_load_state: attribution.loadState, | |
debug_target: attribution.interactionTarget || '(not set)', | |
debug_input_delay: attribution.inputDelay, | |
debug_processing_duration: attribution.processingDuration, | |
debug_presentation_delay: attribution.presentationDelay, | |
debug_processed_event_entries_length: attribution.processedEventEntries.length | |
}, loafData); | |
reportWebVitals(metric, debugData); | |
} | |
function getLoafAttribution(attribution) { | |
var loafEntries = attribution && attribution.longAnimationFrameEntries; | |
var loafEntriesLength = loafEntries && loafEntries.length || 0; | |
if (loafEntriesLength === 0) { | |
return { | |
debug_loaf_meta_length: 0 | |
}; | |
} | |
// The last LoAF entry is usually the most relevant. | |
var loaf = loafEntries[loafEntriesLength - 1]; | |
var loafEndTime = loaf.startTime + loaf.duration; | |
var loafAttribution = { | |
// Stats for the LoAF entry itself. | |
debug_loaf_entry_start_time: loaf.startTime, | |
debug_loaf_entry_end_time: loafEndTime, | |
debug_loaf_entry_work_duration: loaf.renderStart ? loaf.renderStart - loaf.startTime : loaf.duration, | |
debug_loaf_entry_render_duration: loaf.renderStart ? loafEndTime - loaf.renderStart : 0, | |
debug_loaf_entry_pre_layout_duration: loaf.styleAndLayoutStart ? loaf.styleAndLayoutStart - loaf.renderStart : 0, | |
debug_loaf_entry_style_and_layout_duration: loaf.styleAndLayoutStart ? loafEndTime - loaf.styleAndLayoutStart : 0, | |
// LoAF metadata. | |
debug_loaf_meta_length: loafEntriesLength, | |
debug_loaf_meta_script_length: loaf.scripts.length, | |
}; | |
// Stats across all LoAF entries and scripts. | |
var interactionTime = attribution.interactionTime; | |
var inputDelay = attribution.inputDelay; | |
var processingDuration = attribution.processingDuration; | |
var totalStyleAndLayout = null; | |
var totalForcedStyleAndLayout = null; | |
var phases = {}; | |
loafEntries.forEach(function processLoafEntries(loafEntry) { | |
totalStyleAndLayout += loafEndTime - loafEntry.styleAndLayoutStart; | |
loafEntry.scripts.forEach(function processScriptEntries(script) { | |
var scriptEndTime = script.startTime + script.duration; | |
if (scriptEndTime < interactionTime) { | |
return; | |
} | |
totalForcedStyleAndLayout += script.forcedStyleAndLayoutDuration; | |
var blockingDuration = scriptEndTime - Math.max(interactionTime, script.startTime); | |
var invokerType = script.invokerType.replace('-', '_'); | |
var phase = 'phase2'; | |
if (script.startTime < (interactionTime + inputDelay)) { | |
phase = 'phase1'; | |
} else if (script.startTime > (interactionTime + inputDelay + processingDuration)) { | |
phase = 'phase3'; | |
} | |
if (!(phase in phases)) { | |
phases[phase] = {}; | |
} | |
if (!(invokerType in phases[phase])) { | |
phases[phase][invokerType] = 0; | |
} | |
phases[phase][invokerType] += blockingDuration; | |
}); | |
}); | |
Object.entries(phases).forEach(function processPhases(phaseEntry) { | |
var phase = phaseEntry[0]; | |
var invokers = phaseEntry[1]; | |
Object.entries(invokers).forEach(function processInvokers(invokerEntry) { | |
var invoker = invokerEntry[0]; | |
var blockingDuration = invokerEntry[1]; | |
loafAttribution['debug_loaf_' + phase + '_' + invoker] = blockingDuration; | |
}); | |
}); | |
loafAttribution.debug_loaf_total_style_layout_duration = totalStyleAndLayout; | |
loafAttribution.debug_loaf_entry_total_forced_style_and_layout_duration = totalForcedStyleAndLayout; | |
// Stats for the slowest script in the LoAF entry. | |
var scriptAttribution = {}; | |
var slowestScriptDuration = 0; | |
loaf.scripts.forEach(function forEachScript(script) { | |
var scriptEndTime = script.startTime + script.duration; | |
var blockingDuration = scriptEndTime - Math.max(interactionTime, script.startTime); | |
if (interactionTime < scriptEndTime && blockingDuration <= slowestScriptDuration) { | |
return; | |
} | |
slowestScriptDuration = blockingDuration; | |
var phase = 'processing'; | |
if (script.startTime < (interactionTime + inputDelay)) { | |
phase = 'input delay'; | |
} else if (script.startTime > (interactionTime + inputDelay + processingDuration)) { | |
phase = 'presentation'; | |
} | |
scriptAttribution = { | |
debug_loaf_script_phase: phase, | |
debug_loaf_script_blocking_duration: blockingDuration, | |
debug_loaf_script_total_duration: script.duration, | |
debug_loaf_script_compile_duration: script.executionStart - script.startTime, | |
debug_loaf_script_exec_duration: script.startTime + script.duration - script.executionStart, | |
debug_loaf_script_forced_style_and_layout_duration: script.forcedStyleAndLayoutDuration, | |
debug_loaf_script_pause_duration: script.pauseDuration, | |
debug_loaf_script_type: script.invokerType, | |
debug_loaf_script_invoker: script.invoker, | |
debug_loaf_script_source_url: script.sourceURL, | |
debug_loaf_script_source_function_name: script.sourceFunctionName, | |
debug_loaf_script_source_char_position: script.sourceCharPosition, | |
} | |
}); | |
return Object.assign(loafAttribution, scriptAttribution); | |
} | |
function formatData(debugData) { | |
return Object.fromEntries(Object.entries(debugData).map(function formatDataEntry(kv) { | |
var k = kv[0]; | |
var v = kv[1]; | |
// Convert all floats to ints. | |
return [k, typeof v == 'number' ? Math.round(v) : v]; | |
})); | |
} | |
var script = document.createElement('script'); | |
script.src = | |
'https://unpkg.com/web-vitals/dist/web-vitals.attribution.iife.js'; | |
script.onload = function () { | |
webVitals.onLCP(reportLCP); | |
webVitals.onCLS(reportCLS); | |
webVitals.onINP(reportINP); | |
}; | |
document.head.appendChild(script); | |
})(); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment