When displaying images in our web applications we never want to see a dead image shown when an image file has been moved or deleted. This can become a more prevalent problem when we are using dynamic image URLs or convention based URL structure. For example, we might always load a User's profile image by combining their ID number and Last name to form the filename and always looking in a specific assets folder -
The way to avoid having these dead image placeholders is to have a standard fallback image that we can use in place of the missing picture - often this is a grey silhouette of a head. This is all well and good but we generally don't know that an image is missing until it's too late - when the request for the image returns a 404.
We solve this problem by using the error event of the
Ext.dom.Element class to recognise the failure to load the image. This event encapsulates the error process of the underlying
<img> element - onerror. When this event fires we can then replace the
src attribute with the fallback placeholder image. This seems like a pretty simple solution (and it is!) but it's very effective in keeping our app's images nice and tidy.
We're going to demonstrate how to implement this by creating an extension of the
Ext.Img class which is present in both the Classic and Modern toolkits, so makes this a great Universal solution, albeit with a few toolkit specific tweaks!
So, as I've said we're going to make a new component that extends the
Ext.Img class. Let's kick off and lay down the initial class code. You can explore the working code in the following Sencha Fiddle
We'll also make a quick test case to show the component in action - nothing fancy here, just rendering an image to the
<body> with a dud
As we expect this will result in a broken image placeholder being shown:
Reacting to a Broken Image
As we said earlier we need to use the
error event of the Ext.dom.Element class to determine if the image failed to load.
Ext.Img class caches a reference to the rendered
<img> element in the
imgEl property in the Classic toolkit which we can tap into from the
onRender method. We will override this method and add a listener to the event.
With this code in place we can rerun it and see the console log output in the Developer Tools.
The Modern toolkit uses a slightly different approach so we must adapt our solution slightly. The
Ext.Img class in Modern can use both a real
<img> element or a background image - this is determined by the
mode config. This means we must cater for both scenarios. The other different is that it already taps into the
error event so we must override it's existing functionality.
To do this we override the
onError method - we can add this to our Universal solution because in the Classic toolkit this method will just sit idle, whereas, in the Modern toolkit, the code we added in the last step will be unused. This isn't ideal but is neater than having lots of code spread around the project.
If you look in the framework's source we can see the
onError function is defined as below:
Replacing the Broken Image
Now that we can tell when an image hasn't loaded properly we can then replace it with an image that we know is there and will serve as an adequate fallback. We do this by simply setting the
src config (using the
setSrc setter) in this
error event handler and have that one display instead.
Rather than hardcode this value we will add a
config value -
fallbackImg - which can then we easily customised on a per image basis. In this case I've used my default Gravatar profile image.
For the Classic toolkit we simply make this call in the
When we now run our example we can see the two image requests being made and our
console.log call happening in between them.
To add this functionality to the Modern toolkit we must replace part of the
onError method and call the
setSrc function instead.
This will replicate the Classic toolkit's functionality in the Modern. Perfect!
The complete code for the component can be found below. You're most welcome to use it wherever you like!
To see the code in action or to have a play with it, check out this Sencha Fiddle - https://fiddle.sencha.com/#fiddle/14dc
So there we are! We've created an extremely simple method of ensuring our app never has any dead images, keeping it much more professional looking and our users happy! All the while making it Universal so it works across both the Classic and Modern Toolkits.
Taking it Further...
I've kept this very simple to demonstrate the process but we could extend it further to have more features for example....
Image class), once that has loaded then we can swap the URLs.
Enhance the class so that it only loads the real image when it comes into view - this could be really useful when being included in long lists as it will keep the data transfer overhead to a minimum - perfect for mobile devices!
Let me know if you've solved this problem in a different/better way in the past - I'd love to hear from you!