Comparison
DOMContentLoadedvsFirst contentful paint
DOMContentLoaded
your script ran before the images finished, because it only waited for the markup to be parsed.
The event fired when the HTML has been fully parsed and deferred scripts have run, without waiting for stylesheets, images or subframes. It is the one to use for anything that needs the DOM to exist; `load` waits for every subresource and can be seconds later on an image-heavy page. Neither is a good proxy for 'the page is ready for the user', which is what the Core Web Vitals metrics exist to measure instead.
Full entry →First contentful paint
the first text or image appeared, so the user finally knew the page was alive.
The moment anything from the DOM is painted — any text, image, canvas or non-white background. It answers 'is it working', which is a different question from 'can I see what I came for'. It is driven almost entirely by time to first byte plus render-blocking resources, which makes it the metric most improved by removing things from the head.
Full entry →