Optimizing Your Website with Bootstrap: CSS optimization

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 Chapters 9, List Groups and Accordions, 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.

1. Inline styles

After reading this chapter, if you only remember one thing, then let it be that inline styles are bad. Period. Avoid using them whenever possible. Why? That’s 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 class=”d-block img-fluid” src=”images/brazil.png”

data-modal-picture=”#carousel-modal”>

<div class=”carousel-caption”>

Brazil

</div>

</div>

<div style=”height: 400px” class=”carousel-item”>

<img class=”d-block img-fluid” src=”images/datsun.png”

data-modal-picture=”#carousel-modal”>

<div class=”carousel-caption”>

Datsun 260Z

</div>

</div>

<div style=”height: 400px” class=”carousel-item”>

<img class=”d-block img-fluid” src=”images/skydive.png”

data-modal-picture=”#carousel-modal”>

<div class=”carousel-caption”>

Skydive

</div>

</div>

</div>

Note 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 style=”height: 400px” class=”carousel-item active”>

<img class=”d-block img-fluid” src=”images/brazil.png”

data-modal-picture=”#carousel-modal”>

<div class=”carousel-caption”>

Brazil

</div>

</div>

<div style=”height: 400px” class=”carousel-item”>

<img class=”d-block img-fluid” src=”images/datsun.png”

data-modal-picture=”#carousel-modal”>

<div class=”carousel-caption”>

Datsun 260Z

</div>

</div>

<div style=”height: 400px” class=”carousel-item”>

<img class=”d-block img-fluid” src=”images/skydive.png”

data-modal-picture=”#carousel-modal”>

<div class=”carousel-caption”>

Skydive

</div>

</div>

</div>

That’s great! Not only is our CSS now easier to maintain, but 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 that 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.

2. Long identifier and class names

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 removing them altogether. One handy technique of 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, and 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:

  • The services-events-content is an indirect descendent of a div with the id services-events. We cannot remove this id as it is required for the menu to work.
  • The element with the 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? It’s easy—use #services-events div div div. Also, 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();

});

Now, save and refresh. What’s that? As you clicked on a page, the pagination control disappeared; that’s 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 role=”tabpanel” class=”tab-pane active” id=”services-events”>

<div class=”container”>

<div class=”row”>

<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>

Now save and refresh. That’s better! Last but not least, let’s update myphoto.css. Take the following code into consideration:

#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 the following:

#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! However, we have not really improved the performance of our selector. jQuery executes selectors from right to left, hence executing the last selector first. In this example, jQuery will first scan the complete DOM to discover all div elements (last selector executed first) and then apply a filter to return only those elements that are div, with a div parent, and then select only the ones with ID services-events as parent. While we can’t really improve the performance of the selector in this case, we can still simplify our code further by adding a class to each page:

<div id=”page-1″ class=”page”>…</div>

<div id=”page-2″ class=”page”>…</div>

<div id=”page-3″ class=”page”>…</div>

Then, all we need to do is select by the given class: $(‘#services-events div.page’).hide();.

Alternatively, knowing that this is equal to the DOM element within the .on callback, we can do the following in order to prevent us from iterating through the whole DOM: $(this).parents(‘#services-vents’).find(‘.page’).hide();

The final code will look as follows:

$(‘#services-events-pagination’).bootpag({

total: 10

}).on(“page”, function(event, num) {

$(this).parents(‘#services-events’).find(‘.page’).hide();

$(‘#page-‘ + num).show();

});

Note a micro-optimization in the preceding code—there was no need for us to create that var in memory. Hence, the last line changes to $(‘#page-‘ + num).show();.

3. Shorthand rules

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;

4. Grouping selectors

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;

}

Note 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 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;

}

Voila! We just saved 93 bytes! (assuming UTF-8 encoding).

5. Rendering times

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 recalculate the page layout and also draw the changes. Optimizing your page’s rendering times is a complex topic, and as such is 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.

Source: Jakobus Benjamin, Marah Jason (2018), Mastering Bootstrap 4: Master the latest version of Bootstrap 4 to build highly customized responsive web apps, Packt Publishing; 2nd Revised edition.

Leave a Reply

Your email address will not be published. Required fields are marked *