Understanding How the Browser Works

Excerpt from Lean Websites

By understanding how the browser renders your content, you'll gain a lot of insight into how to structure and order your page elements. The browser has the task of loading assets, called HTTP requests. As we've seen from dissecting an HTTP transaction, each asset has several metrics attached to it to help measure its performance.

The following process diagram shows what steps the browser goes through to render a page and its assets. Rendering‚ÄĒthe process of displaying the requested content in the browser window‚ÄĒis the primary function of a browser:

How the browser renders

Figure 5.1. How the browser renders

Let's go through the diagram shown in Figure 5.1:

HTML

HTML parsing is a crucial piece in the rendering puzzle and leads to the construction of the document object model. Parsing happens incrementally, so that the browser can discover required and upcoming requests while sending the necessary responses.

Document Object Model

The browser parses the HTML document to construct a document object model (DOM)‚ÄĒa tree structure that describes the elements on the page.

CSS

The CSS is render blocking, which means that the browser needs to load all CSS files referenced in the HTML markup prior to rendering the content. Without styling, a page can be impractical and sometimes even unusable. An effect called the ‚Äúflash of unstyled content‚ÄĚ (FOUC) could happen if your CSS is loaded too far towards the bottom of the page.

CSS Object Model

In addition to parsing the HTML, the browser also has to process the style sheets (the CSS) in order to understand how the elements in the DOM should be displayed. We refer to this as the CSS Object Model (CSSOM). Both the DOM and the CSSOM together allow the browser to paint the page.

JavaScript

JavaScript adds the power to manipulate and interact with the DOM, causing the render tree to change. If that happens, rendering is blocked until the browser has parsed and executed the script. JavaScript can be loaded synchronously, blocking the DOM, or asynchronously, which will not interrupt document parsing. JavaScript won't run until the CSSOM is fully loaded.

Different browsers run on different JavaScript engines. Depending on your users' browsers, they might experience the performance of your website differently, especially if it's a heavy JavaScript web application.

Render Tree

The render tree combines the content and style information of the entire visible content on the screen. It is the event where the DOM and CSSOM come together.

Not all nodes from the DOM will end up in the render tree. For example, scripts and meta tags are non-visible elements and won't be seen by the user.

In addition, the visibility: hidden style declaration won't insert its child(ren) into the render tree, but it will be rendered and displayed as an empty box. Elements (nodes) using the display:none style declaration, however, won't be added to the render tree.

Layout

While the render tree gets us closer to actually seeing content on the screen, there is still something important missing: this process doesn't define the position or size of each node on the page. That is what the layout step takes care of.

Paint

The final step is to paint the pixels. The style sheets define how expensive the paint process will be. If you have complicated styles, such as drop shadows or CSS animations, this could add complexity to your render tree, resulting in a slower load time. The paint step will reveal if your website, when scrolled, is jank-free or not. Repainting is expensive for the graphics processing unit (GPU) and can cause your page to feel clunky when scrolling.

Critical Rendering Path

By going through the page rendering process above, we have actually traversed through what's known as the critical rendering path (CRP). The CRP describes the code and resources that are required to render the initial view of a page‚ÄĒor the visible, above the fold part.

Let’s traverse through the CRP with a basic example:

1. <html>
2.  <head>
3.      <link rel="stylesheet" href="style.css">
4.  </head>
5.  <body>
6.      <div>Hello Friends!</div>
7.      <img src="bbinto.jpg">
8.      <script src="load_map.js"></script>
9.  </body>
10. </html>

There are several steps that happen to render this page:

  1. The browser begins to construct the DOM after it receives the HTML. That’s also when the browser discovers the link tag and sends the request to retrieve the CSS.

  2. The CSSOM can only be built when CSS has arrived, which is why the CSS is render blocking. The DOM building can’t be finished yet, because JavaScript has not been parsed.

  3. Once the CSS arrives, the browser can build the CSSOM, which then unblocks the JavaScript.

  4. Now the JavaScript can load, which unblocks the DOM to finish. The browser will merge DOM and CSSOM into the Render Tree.

  5. Finally, the browser can complete the Layout and Paint steps.

Figure 5.3 illustrates what's happening as the browser completes these steps:

The critical rendering path

Figure 5.3. The critical rendering path

Optimizing the Critical Rendering Path

We've just discussed how the browser works, what the CRP is, and what each step of the rendering process entails. Now let's apply this knowledge by identifying opportunities to optimize the CRP. Understanding how the browser constructs the CRP puts you at a huge advantage when planning how and what to load on your page. Anything that is not absolutely essential for serving the initial view of a page should not be on the CRP.

The goal is to improve the time to first render the page by prioritizing the visible content of the page. To optimize the CRP, first and foremost, you will need to minimize critical resources. In order to do that you will need to identify and remove render blocking assets as much as possible.

Latency is one of the biggest bottlenecks for serving fast websites. One way of helping to conquer this issue is to focus on visible (ATF) content first. If we can identify what is shown to the user above the fold in the browser, we can focus on that piece first when the page is loaded. Removing styles not needed for the ATF part of the page can drastically reduce the amount of time it takes to render the page. Since CSS is render blocking, anything that we can remove from the CSS will improve our render times.

Follow the 14KB Rule

In the spirit of focusing on the visible content first, that also means focusing on the code that is at the top of an HTML page. Have you heard of the 14KB rule?

In Chapter¬†3, we discussed HTTP transactions. The first part of that is the initial connection. It's the time that the client and browser start to communicate, performing a ‚Äúhandshake‚ÄĚ. Each communication back and forth, before sending data, is defined as a round trip.

It is better to serve as much valuable content as you can within the first round trip. Having said that, only 14KB of the HTML can be transferred before a new round trip.

We should therefore send the most critical data to render first‚ÄĒpresumably the ATF content‚ÄĒwithin the first 14KB. This way we can avoid additional round trips in order to show the first bits and pieces of the page.

If you follow this 14KB rule, your Start Render time will be improved, as it will correlate with the first round trip time. Let's look at an example to check how the Start Render time plays out. I used the wrapper of my blog, and included several cute dog pictures. The page consists of a hero image (the wide image at the top), several dog images, and further down more styling, including the footer. The screenshot in Figure 5.4 displays the page with ATF content highlighted:

Our sample page, with the ATF area marked, as well as some additional styles outside the ATF

Figure 5.4. Our sample page, with the ATF area marked, as well as some additional styles outside the ATF

There are several tools available to help you extract styles for your ATF content from your style sheets, and instead embed (or ‚Äúinline‚ÄĚ) them within the head of your HTML page. I used a bookmarklet that was introduced by Paul Kinlan, a smart engineer at Google, to help me identify the critical ATF styles. You could also use the Critical Path CSS Generator instead.

  1. Load the page in a browser with the bookmarklet installed

  2. Click the bookmarklet, and voilà, the critical CSS appears in the console

  3. Copy the output into the head of the page, wrapped in a style element

  4. Move the rest of the CSS to the bottom of the page

Let's not stop here! Before I show you the result of the improvements thus far, I want to optimize the CRP even further by cleaning up the head.

You can do this by moving scripts to the bottom of the page and revisiting used style sheets. If you use different styles for different media (such as print vs. screen), make sure to use the media attribute to distinguish them. By specifying the media attribute for a print version of the page, you can be assured that the print.css style sheet won't be render blocking. However, note that the file will still be downloaded by the browser.

You could link to your print style sheet like this:

<link rel='stylesheet' href='http://www.bbinto.me/wp-includes/css/
‚ě•print.css' type='text/css'/>

However, it's better to include the media attribute, as shown below:

<!-- print.css stylesheet is not render blocking -->
<link rel='stylesheet' href='http://www.bbinto.me/wp-includes/css/
‚ě•print.css' type='text/css' media='print' />

Here was the head prior to cleanup. Quite messy, don't you think?

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,
‚ě• initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>
<title>bbinto.me</title>
<link rel='stylesheet' id='theme_stylesheet-css'  href='http://www.b
‚ě•binto.me/wp-content/themes/matheson/style.css' type='text/css'
‚ě• media='all' />
<link rel='stylesheet' href='http://www.bbinto.me/wp-includes/css/
‚ě•print.css' type='text/css' media='all' />
<link rel='stylesheet' id='google_fonts-css'  href='//fonts.googleap
‚ě•is.com/css?family=Raleway|Open+Sans:400,400italic,700,700italic'
‚ě• type='text/css' media='all' />
<link rel='stylesheet' id='font_awesome-css'  href='http://www.bbint
‚ě•o.me/wp-content/themes/matheson/library/css/font-awesome.css' type
‚ě•='text/css' media='all' />
<style>
.boxed #page { max-width: 1172px; }
.container { max-width: 992px; }
</style>
<script type='text/javascript' src='https://ajax.googleapis.com/ajax
‚ě•/libs/jquery/1.11.0/jquery.min.js'></script>
</head>

Moving unnecessary code out of the head to keep it as clean as possible will improve your loading time. Let's look at the cleaned-up head now:

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=
‚ě•1.0">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>
<title>bbinto.me</title>
<link rel="dns-prefetch" href="assets.dogtime.com" />
<link rel="dns-prefetch" href="fonts.googleapis.com" />
<link rel="dns-prefetch" href="google-analytics.com" />
<style> /* critical inline css */
*,::after,::before{box-sizing:border-box}*{word-wrap:break-word;outl
‚ě•ine:0}html{font-family:sans-serif;font-size:62.5%;-webkit-tap-high
‚ě•light-color:transparent}body{margin:0;font-family:'Helvetica Neue'
‚ě•,Helvetica,Arial,sans-serif;font-size:14px;line-height:
...
</style>
</head>

Here is the footer:

<link rel='stylesheet' id='theme_stylesheet-css'  href='http://www.b
‚ě•binto.me/wp-content/themes/matheson/style.css?ver=3.9.2' type='tex
‚ě•t/css' media='all' />
<link rel='stylesheet' href='http://www.bbinto.me/wp-includes/css/
‚ě•print.css' type='text/css' media='print' />
<link rel='stylesheet' id='google_fonts-css'  href='//fonts.googleap
‚ě•is.com/css?family=Raleway|Open+Sans:400,400italic,700,700italic'
‚ě• type='text/css' media='all' />
<link rel='stylesheet' id='font_awesome-css'  href='http://www.bbint
‚ě•o.me/wp-content/themes/matheson/library/css/font-awesome.css' type
‚ě•='text/css' media='all' />
<script type='text/javascript' src='https://ajax.googleapis.com/ajax
‚ě•/libs/jquery/1.11.0/jquery.min.js'></script>

It's show time. Let's check out the WPT video comparison to see the improvement:

WPT video comparison of our sample page, showing before and after optimization

Figure 5.5. WPT video comparison of our sample page, showing before and after optimization

Test Speed Index Start Render Visually Complete
Without optimized CRP 3400 3.4s 3.4s
With optimized CRP 1608 0.7s 2.3s

So to summarize what we've done for the page above:

  • We focused on the order of rendering assets by loading critical assets as early as possible

  • We kept scripts and styles in the head to a minimum, allowing only critical CSS in the head

  • We kept the HTML as clean as possible

  • We moved non-critical scripts to the bottom of the page

Get instant access to all books and courses.

Free Trial