HTML Image tags and onerror javascript handlers

HTML Image tags and onerror javascript handlers

03/05/2014 22:30:32

I’ve been doing web stuff for a long, long time (since about 1997) and it never ceases to amaze me that sometimes the most trivial things can pass you by.

I was troubleshooting some weird behaviour on a client site this week – some WebDriver automation tests would sporadically hang forever, seemingly waiting for Amazon CloudFront to serve a file. Calling bullshit on the theory that "oh, CloudFront is just being funny", we decided to dig a little deeper and see why exactly there were images that upon failing to load, would hang forever without timeouts.

Like a good little solider, I skipped all the diagnostics and just went and looked at the code, and discovered an image tag that looked like this:

<img src="some-broken-image-link.jpg" onerror="LoadDefaultImage(); alt="caption" />

I'm not going to lie, my first response was “that cannot possibly work, I’ve never seen anything that looks like that before” – but lo and behold, after visiting a W3Schools link older than time itself, it appears that in HTML (3+? 4+?) all image tags, by default, have onerror javascript handlers baked in that get invoked if the image returns a non-200 status code.

When you work in technology, everyday really is a school-day, and things that apparently are obvious, are frequently completely unknown to you. So I did a couple of cursory google searches…

“img src”: About 12,110,000,000 results (0.21 seconds) 
“img onerror”: About 7,270,000 results (0.21 seconds)

So, about 0.06% of people out there that have heard of img tags, have heard of its onerror handler, which probably qualifies it for a blog post.

Why is this useful?

You like making websites? You have a load of user generated content? You know what sucks? Broken images. They break your design, they make everything look ugly, and they take time to be requested and time-out. Like your websites to be fast? You’re going to want to get rid of these dead images, and the first step in getting rid of them, is knowing about them.

Firstly, you can use the onerror attribute of the img tag to change the img.src and swap that nasty red cross for a nicer default image that doesn’t break your layout.

Secondly, you could go a step further and fire an analytics event to let you know you’ve got dead images rendering in your pages so you can fix them.

Thirdly, with a bit of javascript magic, you can use HTML5 data attributes to “safely load” images that may or may not exist, making sure you switch out bad images for nice defaults without anyone ever noticing.

I put together a trivial example for my client of a page that does just that, has a bunch of images that may or may not exist, and swaps them out silently for a nice “not found” image when the DOM is ready.

<html>
<head>
	<title>Image errors</title>
</head>
<style>
	.some-style {
		border: 10px solid black; 
		width: 100px; 
		height: 100px;
	}
	.safelyLoadImage {
		display: none;
	}
</style>
<body>

<img src="" data-imgsrc="something.jpg" class="some-style safelyLoadImage" />

<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script> <script> $(function(){

	var notFoundImage = "http://upload.wikimedia.org/wikipedia/en/thumb/d/da/Ziltoidtheomniscientcover.jpg/220px-Ziltoidtheomniscientcover.jpg";
	var realImageSrc = $(".safelyLoadImage").data("imgsrc");
			
	$(".safelyLoadImage").attr("onerror", "this.onerror=null; this.src='" + notFoundImage + "';");			
	$(".safelyLoadImage").attr("src", realImageSrc);
	$(".safelyLoadImage").removeClass("safelyLoadImage");		
});

</script>

</body> </html>

 

What was the weird timeout thing in the end?

Turns out, if you don’t remote the img tags onerror handler, and then change the img.src to another image that fails to load, most browsers get into a nasty loop – that’s why in the code sample above we’re setting this.onerror=null; in the onerror handler. Suffice to say, WebDriver wasn’t a huge fan of infinitely loading broken images.

Broken images be damned.