A few months ago, I ran an experiment to see how much faster I could
make one of my websites in less than two hours of work. After installing
a handful of WordPress plugins and fixing a few simple errors, I had
improved the website’s loading speed from 1.61 seconds to 583
milliseconds. That’s a 70.39% improvement, without having made any
visual changes to the website.
According to a 2009 Akamai study,
47% of visitors expect a page to load in under 2 seconds, and 57% of
visitors will abandon a page that takes more than 3 seconds to load.
Since this study, no shortage of case studies have confirmed that
loading time affects sales.
In 2006, Amazon reported that a 100-millisecond increase in page
speed translated to a 1% increase in its revenue. Just a few years
later, Google announced in a blog post that its algorithm takes page speed into account when ranking websites.
Below are twelve quick fixes that will dramatically improve your website’s loading time, including:
identifying which plugins are slowing down your website;
automatically compressing Web pages, images, JavaScript and CSS files;
keeping your website’s database clean;
setting up browser caching the right way.
Lay The Foundation
When your house is sinking into the ground, you don’t polish the
windows — you fix the foundations. The same goes for your website. If
it’s hosted on a sluggish server or has a bloated theme, quick fixes
won’t help. You’ll need to fix the foundation.
So, let’s start with what makes for a good foundation and how to set ourselves up for a website that runs at lightening speed.
Choose A Good Host
Your Web hosting company and hosting package have a huge impact on
the speed of your website, among many other important
performance-related things. I used to be sucked in by the allure of free
or cheap hosting, but with the wisdom of hindsight, I’ve learned that
hosting isn’t an area to skimp on.
To put this into perspective, two of my clients have similar websites
but very different hosting providers. One uses WPEngine (an excellent
hosting company), and the other hosts their website on a cheap shared
server.
The DNS response time (i.e. the time it takes for the browser to
connect to the hosting server) of the client using WPEngine is 7
milliseconds. The client using the cheap shared hosting has a DNS
response time of 250 milliseconds.
If you want your website to run quickly, start with a good hosting company and package.
Choose A Good Theme
Unfortunately, not all WordPress themes are created equal. While some
are extremely fast and well coded, others are bloated with hundreds of
bells and whistles under the pretence of being “versatile and
customizable.”
A few years ago, Julian Fernandes of Synthesis ran an interesting
case study in which he updated his theme from WordPress’ default to the
Genesis framework, monitoring page speed. He noticed that just by
changing the theme to Genesis, his loading time improved from 630 to 172
milliseconds.
When you choose a theme, check the page speed of the theme’s demo,
using a tool such as Pingdom, to see how quickly it runs with nothing
added to it. This should give you an idea of how well coded it is.
Use A Content Delivery Network
I recently started using a content delivery network (CDN) for one of
my websites and noticed a 55% reduction in bandwidth usage and a huge
improvement in page-loading speed.
A CDN hosts your files across a huge network of servers around the
world. If a user from Argentina visits your website, then they would
download files from the server closest to them geographically. Because
your bandwidth is spread across so many different servers, the load on
any single server is reduced.
Setting up a CDN can take a few hours, but it’s usually one of the quickest ways to dramatically improve page-loading speed.
12 Quick Fixes To Speed Up WordPress
Now that our foundation is solid, we can begin fine-tuning our website.
A good way to start speeding up a website is to look at what can be
removed. More often than not, a website is slow not because of what it
lacks but because of what it already has.
1. Identify Plugins That Are Slowing You Down
P3 is one of
my favourite diagnostic plugins because it shows you the impact of your
other plugins on page-loading time. This makes it easy to spot any
plugins that are slowing down your website. (Large preview)
A common culprit is social-sharing plugins, most of which bloat
page-loading times and can easily be replaced by embedding social
buttons into the theme’s source code.
Once you’re aware of which plugins are slowing down your website, you
can make an informed decision about whether to keep them, replace them
or remove them entirely.
2. Compress Your Website
When you compress a file on your computer as a ZIP file, the total
size of the file is reduced, making it both easier and faster to send to
someone. Gzip works in exactly the same way but with your Web page
files.
Once installed, Gzip automatically compresses your website’s files as
ZIP files, saving bandwidth and speeding up page-loading times. When a
user visits your website, their browser will automatically unzip the
files and show their contents. This method of transmitting content from
the server to the browser is far more efficient and saves a lot of time. (Large preview)
There is virtually no downside to installing Gzip, and the increase
in speed can be quite dramatic. As we can see in the screenshot above, MusicLawContracts.com goes from 68 KB to only 13 KB with Gzip installed.
While some plugins will add Gzip to your website with the click of a
button, installing it manually is actually very simple. Open your .htaccess file (found in the root directory on your server), and add the following code to it:
Once you’ve added this snippet of code to .htaccess, test whether Gzip is working on your website by running Check Gzip Compression.
If for whatever reason the code above doesn’t work, try one of the
other methods that Patrick Sexton describes in his article “Enable Gzip.”
3. Compress Images
Images take up the majority of bandwidth on most websites. WP Smush.it
is another great plugin that automatically compresses images as you
upload them to the media library. All compression is “lossless,” meaning
that you won’t notice any difference in the quality of images.
One nice thing about WP Smush.it is that it works retroactively. If
thousands of images are saved in your media library, you can run them
all through the plugin, compressing them to a more manageable size.
4. Leverage Browser Caching
Browser caching is a tricky issue. A handful of great caching plugins
are available, but if set up incorrectly, they could cause more harm than good.
Expires headers tell the browser whether to request a particular file
from the server or from the browser’s cache. Of course, this only works
if the user already has a version of your Web page stored in their
cache; so, the technique will speed up the website only for those who
have already visited your website.
Expires headers speed up a website in two ways. First, they reduce
the need for returning visitors to download the same files from your
server twice. Secondly, they reduce the number of HTTP requests made.
To do this with a plugin, I recommend using WP Super Cache. However, following an installation guide
is strongly recommended to ensure that you set it up correctly.
Alternatively, you could add expires headers by adding the following
code to your .htaccess file.
#
# associate .js with “text/javascript” type (if not present in mime.conf)
#
AddType text/javascript .js
#
# configure mod_expires
#
# URL: http://httpd.apache.org/docs/2.2/mod/mod_expires.html
#
ExpiresActive On
ExpiresDefault “access plus 1 seconds”
ExpiresByType image/x-icon “access plus 2692000 seconds”
ExpiresByType image/jpeg “access plus 2692000 seconds”
ExpiresByType image/png “access plus 2692000 seconds”
ExpiresByType image/gif “access plus 2692000 seconds”
ExpiresByType application/x-shockwave-flash “access plus 2692000 seconds”
ExpiresByType text/css “access plus 2692000 seconds”
ExpiresByType text/javascript “access plus 2692000 seconds”
ExpiresByType application/x-javascript “access plus 2692000 seconds”
ExpiresByType text/html “access plus 600 seconds”
ExpiresByType application/xhtml+xml “access plus 600 seconds”
#
# configure mod_headers
#
# URL: http://httpd.apache.org/docs/2.2/mod/mod_headers.html
#
Header set Cache-Control “max-age=2692000,public”
Header set Cache-Control “max-age=600,private, must-revalidate”
Header unset ETag
Header unset Last-Modified
5. Clean Up the Database
I’m a big fan of how often WordPress autosaves everything, but the
disadvantage is that your database will get filled with thousands of
post revisions, trackbacks, pingbacks, unapproved comments and trashed
items pretty quickly.
The solution to this is a fantastic plugin called WP-Optimize,
which routinely clears out your database’s trash, keeping the database
efficient and filled only with what needs to be kept. Of course, when
doing anything to your database, always back up first.
6. Minify CSS and JavaScript Files
If you’ve installed more than a handful of plugins, chances are that
your website links to 10 to 20 individual style sheets and JavaScript
files on every page. This is not ideal. Putting all JavaScript into one
JavaScript file and all CSS in one CSS file is considerably more
efficient.
This is where minification comes in. Plugins such as Better WordPress Minify
will combine all of your style sheets and JavaScript files into one,
reducing the number of requests that the browser needs to make.
I prefer Better WordPress Minify because it’s less aggressive than
some of the other plugins that do the same thing (some of which cause
problems, as Hristo Pandjarov outlines).
7. Turn Off Pingbacks and Trackbacks
Pingbacks and trackbacks are methods used by WordPress to alert other
blogs that your posts link to. While sometimes interesting, they can be
a drain on page speed and are usually better turned off. You can turn
them off under the “Discussion” tab in “Settings.”
8. Specify Image Dimensions and Character Sets
Before a visitor’s browser can display your Web page, it has to
figure out how to lay out the content around the images. Without knowing
the size of these images, the browser has to figure it out, causing it
to work harder and take longer.
Specifying image dimensions saves the browser from having to go through this step, speeding things up.
For the same reason, specifying a character set in your HTTP response
headers is useful, so that the browser doesn’t have to spend extra time
working out which one you’re using. Simply add the character set to
your website’s head section.
9. Move CSS to the Top and JavaScript to the Bottom
Linking to your style sheets as close to the top of the page as
possible is widely recommended because browsers won’t render a page
before rendering the CSS file. JavaScript, on the other hand, should be
as close to the bottom of the footer as possible because it prevents
browsers from parsing anything after it until it has full loaded.
In the majority of cases, this simple fix will improve page-loading
speed by forcing files to be downloaded in the optimal order. But it can
cause issues on websites that rely heavily on JavaScript and that
require JavaScript files to load before the user sees any of the page.
10. Use CSS Sprites
A sprite is essentially one large image file that contains all of
your individual images next to each other. Using CSS, you can hide
everything in the image except for the section you need, by specifying a
set of coordinates.
CSS sprites speed up a website because loading one big image is much faster than loading a lot of small images. (Large preview)
The easiest solution is SpriteMe, a tool that turns all of your images into a CSS sprite.
Remember that Safari does not load large sprites, so use William Malone’s calculator to identify whether your sprite is too large.
11. Enable Keep Alive
HTTP Keep Alive refers to the message that is sent between the
client’s machine and the Web server asking for permission to download a
file. Enabling Keep Alive allows the client’s machine to download
multiple files without having to repeatedly ask for permission, thus
saving bandwidth.
To enable Keep Alive, simply copy and paste the code below into your .htaccess file.
Header set Connection keep-alive
12. Replace PHP With Static HTML Where Appropriate
PHP is great for making a website efficient and reducing the need to
enter the same information multiple times. However, calling information
through PHP uses up server resources and should be replaced with static
HTML where it doesn’t save any time.
Conclusion
In the next 12 months, mobile Internet usage is expected to overtake
desktop usage. This shift towards Internet-enabled mobile devices means
that having a fast website has never been as important as it is today.
Users now expect websites to be lightening fast, and developers who
don’t comply will ultimately lose out to developers who invest in
delivering a great experience.
Click, touch, load, drag, change, input, error, resize — the list of
possible DOM events is lengthy. Events can be triggered on any part of a
document, whether by a user’s interaction or by the browser. They don’t
just start and end in one place; they flow though the document, on a
life cycle of their own. This life cycle is what makes DOM events so
extensible and useful. As developers, we should understand how DOM events work, so that we can harness their potential and build engaging experiences.
Throughout my time as a front-end developer, I felt that I was never
given a straight explanation of how DOM events work. My aim here is to
give you a clear overview of the subject, to get you up to speed more
quickly than I did.
I will introduce the basics of working with DOM events, then delve
into their inner workings, explaining how we can make use of them to
solve common problems.
Listening For Events
In the past, browsers have had major inconsistencies in the way they attach event listeners to DOM nodes. Libraries such as jQuery have been invaluable in abstracting away these oddities.
As we move ever closer to standardized browser environments, we can
more safely use the APIs from the official specification. To keep it
simple, I will describe how to manage events for the modern Web. If you
are writing JavaScript for Internet Explorer (IE) 8 or below, I would
advise using a polyfill or framework (such as jQuery) to manage event listeners.
In JavaScript, we can listen to events using this:
event-name (string)
This is the name or type of event that you would like to listen to. It could be any of the standard DOM events (click, mousedown, touchstart, transitionEnd, etc.) or even your own custom event name (we’ll touch on custom events later).
callback (function)
This function gets called when the event happens. The event object, containing data about the event, is passed as the first argument.
use-capture (boolean)
This declares whether the callback should be fired in the “capture”
phase. (Don’t worry: We’ll explain what that means a little later.)
var element = document.getElementById('element');functioncallback(){alert('Hello');}
// Add listener
element.addEventListener('click', callback);
Removing event listeners once they are no longer needed is a best
practice (especially in long-running Web applications). To do this, use
the element.removeEventListener() method:
But removeEventListener has one catch: You must have a reference to the callback function that was originally bound. Simply calling element.removeEventListener('click'); will not work.
Essentially, if we have any interest in removing event listeners
(which we should in “long-lived” applications), then we need to keep a
handle on our callbacks. This means we cannot use anonymous functions.
var element = document.getElementById('element');functioncallback(){alert('Hello once');
element.removeEventListener('click', callback);}
// Add listener
element.addEventListener('click', callback);
An easy gotcha is callbacks being called with the incorrect context. Let’s explain with an example.
var element = document.getElementById('element');var user ={
firstname:'Wilson',
greeting:function(){alert('My name is '+ this.firstname);}};
// Attach user.greeting as a callback
element.addEventListener('click', user.greeting);
// alert => 'My name is undefined'
We expected the callback to correctly alert us with My name is Wilson. In fact, it alerts us with My name is undefined. In order for this.firstName to return Wilson, user.greeting must be called within the context (i.e. whatever is left of the dot when called) of user.
When we pass the greeting function to the addEventListener method, we are only passing a reference to the function; the context of user is not passed with it. Internally, the callback is called in the context of element, which means that this refers to element, not to user. Therefore, this.firstname is undefined.
There are two ways to prevent this context mismatch. First, we can call user.greeting() with the correct context inside an anonymous function.
element.addEventListener('click',function(){
user.greeting(); // alert => 'My name is Wilson'
});
The last method isn’t so good because now we don’t have a handle on the function when we want to remove it with .removeEventListener(). Plus, it’s pretty ugly. I prefer to use the .bind() method (built into all functions, as of ECMAScript 5) to generate a new function (bound) that will always run in the given context. We then pass that function as the callback to .addEventListener().
// Overwrite the original function with
// one bound to the context of 'user'
user.greeting = user.greeting.bind(user);
// Attach the bound user.greeting as a callback
button.addEventListener('click', user.greeting);
We also have a reference to the callback at hand, which we can use to unbind the listener if need be.
The event object is created when the event first happens; it travels
with the event on its journey through the DOM. The function that we
assign as a callback to an event listener is passed the event object as
its first argument. We can use this object to access a wealth of
information about the event that has occurred:
type (string)
This is the name of the event.
target (node)
This is the DOM node where the event originated.
currentTarget (node)
This is the DOM node that the event callback is currently firing on.
bubbles (boolean)
This indicates whether this is a “bubbling” event (which we’ll explain later).
preventDefault (function)
This prevents any default behaviour from occurring that the user agent
(i.e. browser) might carry out in relation to the event (for example,
preventing a click event on an <a> element from loading a new page).
stopPropagation (function)
This prevents any callbacks from being fired on any nodes further along
the event chain, but it does not prevent any additional callbacks of the
same event name from being fired on the current node. (We’ll talk about
that later.)
stopImmediatePropagation (function)
This prevents any callbacks from being fired on any nodes further along
the event chain, including any additional callbacks of the same event
name on the current node.
cancelable (boolean)
This indicates whether the default behaviour of this event can be prevented by calling the event.preventDefault method.
defaultPrevented (boolean)
This states whether the preventDefault method has been called on the event object.
isTrusted (boolean)
An event is said to be “trusted” when it originates from the device itself, not synthesized from within JavaScript.
eventPhase (number)
This number represents the phase that the event is currently in: none (0), capture (1), target (2) or bubbling (3). We’ll go over event phases next.
timestamp (number)
This is the date on which the event occurred.
Many other properties can be found on the event object, but they are
specific to the type of event in question. For example, mouse events
will include clientX and clientY properties on the event object to indicate the location of the pointer in the viewport.
It’s best to use your favorite browser’s debugger or a console.log to look more closely at the event object and its properties.
Event Phases
When a DOM event fires in your app, it doesn’t just fire once where
the event originated; it embarks on a journey of three phases. In short,
the event flows from the document’s root to the target (i.e. capture
phase), then fires on the event target (target phase), then flows back
to the document’s root (bubbling phase). (Image source: W3C) Demo: Slow motion event path
Capture Phase
The first phase is the capture phase. The event starts its journey at
the root of the document, working its way down through each layer of
the DOM, firing on each node until it reaches the event target. The job
of the capture phase is to build the propagation path, which the event
will travel back through in the bubbling phase.
As mentioned, you can listen to events in the capture phase by setting the third argument of addEventListener to true.
I have not found many use cases for capture phase listeners, but you
could potentially prevent any clicks from firing in a certain element if
the event is handled in the capture phase.
var form = document.querySelector('form');
form.addEventListener('click',function(event){
event.stopPropagation();},true); // Note: 'true'
If you’re unsure, listen for events in the bubbling phase by setting the useCapture flag to false or undefined.
Target Phase
An event reaching the target is known as the target phase. The event
fires on the target node, before reversing and retracing its steps,
propagating back to the outermost document level.
In the case of nested elements, mouse and pointer events are always
targeted at the most deeply nested element. If you have listened for a click event on a <div> element, and the user actually clicks on a <p> element in the div, then the <p> element will become the event target. The fact that events “bubble” means that you are able to listen for clicks on the <div> (or any other ancestor node) and still receive a callback once the event passes through.
Bubbling Phase
After an event has fired on the target, it doesn’t stop there. It
bubbles up (or propagates) through the DOM until it reaches the
document’s root. This means that the same event is fired on the target’s
parent node, followed by the parent’s parent, continuing until there is
no parent to pass the event onto.
Think of the DOM as an onion and the event target as the core of the
onion. In the capture phase, the event drills into the onion through
each layer. When the event reaches the core, it fires (the target
phase), and then reverses, working its way back up through each layer
(the propagation phase). Once the event has returned to the surface, its
journey is over.
Bubbling is useful. It frees us from listening for an event on the
exact element it came from; instead, we listen on an element further up
the DOM tree, waiting for the event to reach us. If events didn’t
bubble, we would have to, in some cases, listen for an event on many
different elements to ensure that it is caught. Demo: Identifying event phases
The majority of, but not all, events bubble. When events do not bubble, it is usually for a good reason. If in doubt, check the specification.
Stopping Propagation
Interrupting the path of the event at any point on its journey (i.e.
in the capture or bubbling phase) is possible simply by calling the stopPropagation
method on the event object. Then, the event will no longer call any
listeners on nodes that it travels through on its way to the target and
back to the document.
child.addEventListener('click',function(event){
event.stopPropagation();});
parent.addEventListener('click',function(event){ // If the child element is clicked
// this callback will not fire
});
Calling event.stopPropagation() will not prevent any
additional event listeners from being called on the current target if
multiple listeners for the same event exist. If you wish to prevent any
additional listeners from being called on the current node, you can use
the more aggressive event.stopImmediatePropagation() method.
child.addEventListener('click',function(event){
event.stopImmediatePropagation();});
child.addEventListener('click',function(event){ // If the child element is clicked
// this callback will not fire
});
The browser has default behaviors that will respond when certain
events occur in the document. The most common event is a link being
clicked. When a click event occurs on an <a> element, it will bubble up to the document level of the DOM, and the browser will interpret the href attribute and reload the window at the new address.
In Web applications, developers usually want to manage the navigation
themselves, without causing the page to refresh. To do this, we need to
prevent the browser’s default response to clicks and instead do our own
thing. To do this, we call event.preventDefault().
anchor.addEventListener('click',function(event){
event.preventDefault(); // Do our own thing
});
We can prevent many other default behaviors in the browser. For
example, we could prevent presses of the space bar from scrolling the
page in an HTML5 game, or we could prevent clicks from selecting text.
Calling event.stopPropagation() here will only prevent
callbacks that are attached further down the propagation chain from
being fired. It will not prevent the browser from doing its thing. Demo: Preventing default behavior
Custom Events
The browser is not the only thing that is able to trigger DOM events.
We can create our own custom events and dispatch them on any element in
the document. This type of event would behave just the same as a
regular DOM event.
var myEvent =newCustomEvent("myevent",{
detail:{
name:"Wilson"},
bubbles:true,
cancelable:false});
// Listen for 'myevent' on an element
myElement.addEventListener('myevent',function(event){alert('Hello '+ event.detail.name);});
// Trigger the 'myevent'
myElement.dispatchEvent(myEvent);
Synthesizing “untrusted” DOM events on elements (for example, click)
to simulate user interaction is also possible. This can be useful when
testing DOM-related libraries. If you’re interested, the Mozilla
Developer Network has a write-up on it.
Note the following:
The CustomEvent API is not available in IE 8 and below.
The Flight framework from
Twitter makes use of custom events to communicate between modules. This
enforces a highly decoupled, modular architecture.
Delegate event listeners are a more convenient and performant way to
listen for events on a large number of DOM nodes using a single event
listener. For example, if a list contains 100 items that all need to
respond to a click event in a similar way, then we could
query the DOM for all of the list items and attach an event listener to
each one. This would result in 100 separate event listeners. Whenever a
new item is added to the list, the click event listener would have to be added to it. Not only does this risk getting expensive, but it is tricky to maintain.
Delegate event listeners can make our lives a lot easier. Instead of listening for the click event on each element, we listen for it on the parent <ul> element. When an <li> is clicked, then the event bubbles up to the <ul>, triggering the callback. We can identify which <li> element has been clicked by inspecting the event.target. Below is a crude example to illustrate:
var list = document.querySelector('ul');
list.addEventListener('click',function(event){var target = event.target;while(target.tagName !=='LI'){
target = target.parentNode;if(target === list)return;} // Do stuff here
});
This is better because we have only the overhead of a single event
listener, and we no longer have to worry about attaching a new event
listener when an item is added to the list. The concept is pretty simple
but super-useful.
I wouldn’t recommend using such a crude implementation in your app.
Instead, use an event delegate JavaScript library, such as FT Lab’s ftdomdelegate. If you’re using jQuery, you can seamlessly use event delegation by passing a selector as the second parameter to the .on() method.
// Not using event delegation
$('li').on('click',function(){});
// Using event delegation
$('ul').on('click','li',function(){});
The load event fires on any resource that has finished
loading (including any dependent resources). This could be an image,
style sheet, script, video, audio file, document or window.
window.onbeforeunload enables developers to ask the user
to confirm that they want to leave the page. This can be useful in
applications that require the user to save changes that would get lost
if the browser’s tab were to be accidentally closed.
window.onbeforeunload =function(){if(textarea.value != textarea.defaultValue){return'Do you want to leave the page and discard changes?';}};
Note that assigning an onbeforeunload handler prevents the browser from caching the page, thus making return visits a lot slower. Also, onbeforeunload handlers must be synchronous. Demo: onbeforeunload
Stopping Window bounce in Mobile Safari
At the Financial Times, we use a simple event.preventDefault technique to prevent mobile Safari from bouncing the window when it is scrolled.
Be warned that this will also prevent any native scrolling from working ( such as overflow: scroll).
To allow native scrolling on a subset of elements that need it, we
listen for the same event on the scrollable element and set a flag on
the event object. In the callback at the document level, we decide
whether to prevent the default behavior of the touch event based on the
existence of the isScrollable flag.
// Lower down in the DOM we set a flag
scrollableElement.addEventListener('touchmove',function(event){
event.isScrollable =true;});
// Higher up the DOM we check for this flag to decide
// whether to let the browser handle the scroll
document.addEventListener('touchmove',function(event){if(!event.isScrollable) event.preventDefault();});
Manipulating the event object is not possible in IE 8 and below. As a workaround, you can set properties on the event.target node.
resize
Listening to the resize event on the window object is
super-useful for complex responsive layouts. Achieving a layout with CSS
alone is not always possible. Sometimes JavaScript has to help us
calculate and set the size of elements. When the window is resized or
the device’s orientation changes, then we would likely need to readjust
these sizes.
window.addEventListener('resize',function(){ // update the layout
});
I recommended using a debounced callback to normalize the callback rate and prevent extreme thrashing in the layout. Demo: Window resizing
transitionEnd
Today we use CSS to power the majority of transitions and animations
in our applications. Sometimes, though, we still need to know when a
particular animation has finished.
el.addEventListener('transitionEnd',function(){ // Do stuff
});
Note the following:
If you’re using @keyframe animations, use the animationEnd event name, instead of transitionEnd.
Like a lot of events, transitionEnd bubbles. Remember either to call event.stopPropagation() on any descendant transition events or to check the event.target to prevent callback logic from running when it’s not supposed to.
Event names are still widely vendor-prefixed (for example, webkitTransitionEnd, msTransitionEnd, etc). Use a library such as Modernizr to get the event name’s correct prefix.
The animationiteration event will fire every time a
currently animating element completes an iteration. This is useful if we
want to stop an animation but not midway through.
If an error occurs when a resource loads, we might want to do
something about it, especially if our users are on a flaky connection.
The Financial Times uses this event to detect any images that might have
failed to load in an article and instantly hide them. Because the “DOM Level 3 Events” specification has redefined the error event to “not bubble,” we can handle the event in one of two ways.
Unfortunately, addEventListener does not address all use cases. My colleague Kornel has kindly pointed me to an example that demonstrates that the only way, sadly, to guarantee the execution of image error event callbacks is to use (the often frowned upon) inline event handlers.
The reason for this is that you cannot be sure that the code that binds the error event handler will be executed before the error event actually happens. Using inline handlers means that when the markup is parsed and the image is requested, our error listeners will be attached. Demo: Image error
Lessons From The Event Model
A lot can be learned from the success of the DOM events model. We can
employ similar decoupled concepts in our own projects. Modules in an
application can be as complex as they need to be, so long as that
complexity is sealed away behind a simple interface. Many front-end
frameworks (such as Backbone.js) are heavily event-based, solving
cross-module communication in a publish and subscribe model that is very
similar to the DOM. Event-based architectures are great. They give us a
simple common interface in which to write applications that respond to
physical interactions across thousands of devices! Via events, devices
tell us exactly what has happened and when it occurred, letting us
respond however we please. What goes on behind the scenes is not of
concern; we get a level of abstraction that frees us to get on with
building our awesome app.