You may have seen some web pages with broken images. It diminishes website trustworthiness and it just looks bad. Let’s explore some solutions to broken images and have a reliable fallback in all browsers.
This is how our problem looks like. Note that this line of copy (next to the image icon) comes from the alt
property of the <img>
tag.
See the Pen WLzgpN by Anatoli (@makaroni4) on CodePen.
The most popular solution atm is to use :before/:after
pseudo elements. Idea is that we position pseudo element absolutely over the image and fill it with color/gradient. Here’s a CSS snippet:
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
.img {
/* Otherwise pseudo element will fill
in the whole page (because of width: 100%) */
position: relative;
}
&:before {
width: 100%; /* Make sure we fill in the whole image area */
height: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
/* We want to center copy from alt tag horizontally & vertically */
align-items: center;
justify-content: center;
/* Nice gradient from https://cssgradient.io */
background: linear-gradient(to right, #fa709a 0%, #fee140 100%);
color: #FFF;
font-size: 16px;
font-weight: bold;
font-family: 'Varela Round', sans-serif;
/* Cool trick: we're using CSS attr() function to retrieve alt property copy */
content: attr(alt);
}
This is a live example of this approach:
See the Pen Broken image styling with :before pseudo element by Anatoli (@makaroni4) on CodePen.
It’s very elegant, CSS-only. It has a major drawback though – it doesn’t work in Safari (neither on desktop nor on mobile) ⚠️ For example, this blog has about 30% of visits from Safari. It is simply unacceptable.
It’s quite hard to think of a pure CSS solution which would be 100% cross browser. Let’s use onerror
event handler which is supported by all browsers. We’ll simply replace broken image tag with a div of the same size, fill it with gradient and use alt
tag’s content. IMO it’s the best way because it keeps our <img>
tags untouched (so we don’t need to extra markup manually). Here’s a JS snippet with the solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
document.querySelector(".img").onerror = function() {
const img = this;
img.style.display = "none";
const imgFallback = document.createElement("div");
imgFallback.className = "img-fallback";
imgFallback.innerHTML = img.alt;
imgFallback.style.width = img.width ? img.width + "px" : "300px";
imgFallback.style.height = img.height ? img.height + "px" : "300px";
img.parentNode.insertBefore(imgFallback, img.nextSibling);
}
Here it is in action:
See the Pen Styling broken image with JS by Anatoli (@makaroni4) on CodePen.
Note, that we still use some CSS to style a fallback <div>
block (we gave it a class img-fallback
):
1
2
3
4
5
6
7
8
9
10
11
12
.img-fallback {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(120deg, #f093fb 0%, #f5576c 100%);
color: #FFF;
font-size: 16px;
font-weight: bold;
font-family: 'Varela Round', sans-serif;
}
Let’s sum up. We have ~20 LOC no-external-dependencies solution to keep your website free from broken images. Not so bad, right? 🍻
P.S. If you know a better way to handle broken images – let everyone know in comments.