The following request returns an HTTP status code of 404, but also content-type: image/jpeg
and valid JPEG data in the response body:
https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg
If you set this as the src
of an image, the image will still be displayed. Furthermore, an onerror
event is not triggered, so I can't use onerror="this.src=this.src.replace('maxres', 'sd')"
to trigger a fallback.
This behaviour is consistent across Firefox/Chrome/Edge, so I wonder 1) if this is something defined in the standards? Or is it actually a bug that exists in all major browsers?
Using it as a source in a picture
element will also not have it fall back to the next available image size and display the "broken" image as well.
<h3>Plain image element:</h3>
<img src="https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg" onerror="this.src=this.src.replace('maxres', 'sd')">
<h3>Picture element:</h3>
<picture>
<source media="(max-width: 640px)" srcset="https://img.youtube.com/vi/lXMskKTw3Bc/sddefault.jpg">
<source media="(max-width: 480px)" srcset="https://img.youtube.com/vi/lXMskKTw3Bc/hqdefault.jpg">
<source media="(max-width: 320px)" srcset="https://img.youtube.com/vi/lXMskKTw3Bc/mqdefault.jpg">
<source srcset="https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg">
<img src="https://img.youtube.com/vi/lXMskKTw3Bc/sddefault.jpg" type="image/jpeg">
</picture>
<h3>expected output:</h3>
<img src="https://img.youtube.com/vi/lXMskKTw3Bc/sddefault.jpg">
2) Is there a way to circumvent this and fall back to the next smaller image size that doesn't result in a 404 status, preferrably without using JavaScript?
Thanks to @Tyler Roper for his comment:
The documentation states that
onerror
is thrown when the image "fails to load". In your case, the image has loaded successfully; it's just accompanied by a 404 notifying you that it is not the image that you originally requested.
For my specific case, I solved it this way:
<img
src="https://img.youtube.com/vi/lXMskKTw3Bc/maxresdefault.jpg"
onload="if (this.naturalWidth < 1280) { this.src = this.src.replace('maxres', 'sd') }">
So, it loads the maxresdefault
first, which should normally be at least 1280px wide. If it's less, the image source is changed to sddefault
.