Loosely put, website optimization refers to the activities and processes that improve your website's user experience and visibility while reducing the costs associated with hosting your website. In his book Website Optimization, Andrew B. King summarizes this notion succinctly with the question: "How do we make our website better?" (Website Optimization, Andrew B. King, O'Reilly). As such, the topic has been the sole subject of entire books, and a single chapter barely touches the tip of the iceberg. The topic is one of many facets, ranging from server-side optimization, search engine optimization, pay-per-click optimization, and client-side optimization. In this chapter, we will only discuss the latter. That is, we will be improving the loading and rendering time of MyPhoto. Specifically, this chapter is concerned with:
MyPhoto index.htmlBy the end of this chapter, you will understand the essential techniques behind client-side optimization. Within the context of MyPhoto you will therefore learn how to:
Before we even consider compression, minification, and file concatenation, we should think about the ways in which we can simplify and optimize our existing style sheet without using third-party tools. Of course, we should have striven for an optimal style sheet to begin with, and in many aspects we did. However, our style sheet still leaves room for improvement. Some of these improvements we have ignored on purpose within the previous chapters, as they would have detracted from the chapter's intended purpose. However, as this chapter is concerned with optimizing the client-side code of a web page, the time has come to talk a little about general tips and practices that will help you keep your style sheets small and your rules short. We will address each of these tips and practices in turn.
If, after reading this chapter, you only remember one thing, then please let it be the following: inline styles are bad. Period. Avoid using them whenever possible. Why? Because not only will they make your website impossible to maintain as the website grows, they also take up precious bytes as they force you to repeat the same rules over and over. Consider the following markup for our Gallery section:
<div class="carousel-inner" role="listbox">
<div style="height: 400px" class="carousel-item active">
<img data-modal-picture="#carouselModal" src=
"images/brazil.png">
<div class="carousel-caption">
Brazil
</div>
</div>
<div style="height: 400px" class="carousel-item">
<img data-modal-picture="#carouselModal" src=
"images/datsun.png">
<div class="carousel-caption">
Datsun 260Z
</div>
</div>
<div style="height: 400px" class="carousel-item">
<img data-modal-picture="#carouselModal" src=
"images/skydive.png">
<div class="carousel-caption">
Skydive
</div>
</div>
</div>
Notice how the rule for defining a gallery item's height, style="height: 400px", is repeated three times, once for each of the three gallery items. That's an additional 21 characters (or 21 bytes assuming that our document is UTF-8) for each additional image. Multiplying 3 * 21 gives us 63 bytes. And 21 more bytes for every new image that you want to add to the Gallery. Not to mention that, if you ever want to update the height of the gallery images, you will need to manually update the style attribute for every single image. The solution is of course to replace the inline styles with an appropriate class. Let's go ahead and define an img class that can be applied to any carousel image:
.carousel-item {
height: 400px;
}
Now let's go ahead and remove the style rules:
<div class="carousel-inner" role="listbox">
<div class="carousel-item active">
<img data-modal-picture="#carouselModal" src=
"images/brazil.png">
<div class="carousel-caption">
Brazil
</div>
</div>
<div class="carousel-item">
<img data-modal-picture="#carouselModal" src=
"images/datsun.png">
<div class="carousel-caption">
Datsun 260Z
</div>
</div>
<div class="carousel-item">
<img data-modal-picture="#carouselModal" src=
"images/skydive.png">
<div class="carousel-caption">
Skydive
</div>
</div>
</div>
Great! Not only is our CSS now easier to maintain, we also shaved 29 bytes off our website (the original inline styles required 63 bytes; our new class definition, however, requires only 34 bytes). Yes, this does not seem like much, especially in the world of high-speed broadband. But remember, your website will grow, and every byte adds up.
There are several more inline styles spread around our HTML document. Go ahead and fix them before moving on to the next section.
The longer your strings, the larger your files. It's a no-brainer. As such, long identifier and class names naturally increase the size of your web page. Of course, extremely short class or identifier names tend to lack meaning, and therefore will make it more difficult (if not impossible) to maintain your page. As such, one should strive for an ideal balance between length and expressiveness (we will be covering a handy little tool that will provide you with the benefits of both later on in this chapter). Of course, even better than shortening identifiers, is to remove them altogether. One handy technique for removing these is to use hierarchical selection. Take our events pagination code. For example, we are using the services-events-content identifier within our pagination logic as follows:
$('#services-events-pagination').bootpag({
total: 10
}).on("page", function(event, num){
$('#services-events-content div').hide();
var current_page = '#page-' + num;
$(current_page).show();
});
To denote the services content, we broke the name of our identifier into three parts, namely, services, events, content
. Our markup is as follows:
<div id="services-events-content">
<div id="page-1">
<h3>My Sample Event #1</h3>
...
</div>
</div>
Let's try and get rid of this identifier altogether by observing two characteristics of our Events section:
services-events-content is an indirect descendent of a div with the id services-events. This id we cannot remove, as it is required for the menu to work.id services-events-content is itself a div. If we were to remove its id, we could also remove the entire div.As such, we do not need a second identifier to select the pages that we wish to hide. Instead, all that we need to do is select the div within the div that is within the div that is assigned the id services-events. How do we express this as a CSS selector? Easy. Use #services-events div div div. And as such, our pagination logic is updated as follows:
$('#services-events-pagination').bootpag({
total: 10
}).on("page", function(event, num){
$('#services-events div div div').hide();
var current_page = '#page-' + num;
$(current_page).show();
});
Save and refresh. What's that? As you clicked on a page, the pagination control disappeared. That is because we are now hiding all div elements that are two div elements down from the element with the id services-events. Move the pagination control div outside its parent element. Our markup should now look as follows:
<div class="tab-content bg-myphoto-light">
<div role="tabpanel" class="tab-pane active" id=
"services-events">
<div class="container">
<div class="row" style="margin: 1em;">
<div id="page-1">
<h3>My Sample Event #1</h3>
...
<h3>My Sample Event #2</h3>
...
</div>
<div id="page-2">
<h3>My Sample Event #3</h3>
...
</div>
</div>
<div id="services-events-pagination"></div>
</div>
</div>
</div>
Save and refresh. That's better! Last but not least, let us update myphoto.css. Take the following code:
#services-events-content div {
display: none;
}
#services-events-content div img {
margin-top: 0.5em;
margin-right: 1em;
}
#services-events-content {
height: 15em;
overflow-y: scroll;
}Replace this code with:
#services-events div div div {
display: none;
}
#services-events div div div img {
margin-top: 0.5em;
margin-right: 1em;
}
#services-events div div div {
height: 15em;
overflow-y: scroll;
}
That's it, we have simplified our style sheet and saved some bytes in the process!
According to, the Mozilla Developer Network (shorthand properties, Mozilla Developer Network, https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties, accessed November 2015), shorthand properties are:
"CSS properties that let you set the values of several other CSS properties simultaneously. Using a shorthand property, a Web developer can write more concise and often more readable style sheets, saving time and energy." – Mozilla Developer Network, 2015
Unless strictly necessary, we should never be using longhand rules. When possible, shorthand rules are always the preferred option. Besides the obvious advantage of saving precious bytes, shorthand rules also help increase your style sheet's maintainability. For example, border: 20px dotted #FFF is equivalent to three separate rules:
border-style: dotted;
border-width: 20px;
border-color: #FFF;
Organizing selectors into groups will arguably also save some bytes. Consider lines 80 to 93 in myphoto.css:
.navbar-myphoto .dropdown-menu > a:hover
{
color: gray;
background-color: #504747;
}
.navbar-myphoto .dropdown-menu > a:focus
{
color: gray;
background-color: #504747;
}
.navbar-myphoto .dropdown-menu > .active > a:focus
{
color: gray;
background-color: #504747;
}
Notice how each of the three selectors contains the same declarations. That is, the color and background-color properties are set to the exact same values for each selector. To prevent us from repeating these declarations, we should simply group them (reducing the code from 274 characters down to 181 characters):
.navbar-myphoto .dropdown-menu > a:hover,
.navbar-myphoto .dropdown-menu > a:focus,
.navbar-myphoto .dropdown-menu > .active > a:focus {
color: gray;
background-color: #504747;
}
Voilà! We just saved 93 bytes! (assuming UTF-8 encoding).
When optimizing your style rules, the number of bytes should not be your only concern. In fact, it comes secondary to the rendering time of your web page. CSS rules affect the amount of work that is required by the browser to render your page. As such, some rules are more expensive than others. For example, changing the color of an element is cheaper than changing its margin. The reason for this is that a change in color only requires your browser to draw the new pixels. While drawing itself is by no means a cheap operation, changing the margin of an element requires much more effort. Your browser needs to both re-calculate the page layout and also draw the changes. Optimizing your page's rendering times is a complex topic, and as such beyond the scope of this book.
However, we recommend that you take a look at http://csstriggers.com/ . This site provides a concise overview of the costs involved when updating a given CSS property.
Did you know?
Udacity now offers a free online course on Browser Rendering Optimization. Head over to https://www.udacity.com for more. We cannot recommend the course highly enough!