<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Aysner</title>
    <description>The latest articles on DEV Community by Christian Aysner (@aysner).</description>
    <link>https://dev.to/aysner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F422173%2Fb2d6512d-d8a7-489f-bf52-cc00ecbfbe68.jpg</url>
      <title>DEV Community: Christian Aysner</title>
      <link>https://dev.to/aysner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aysner"/>
    <language>en</language>
    <item>
      <title>Interactive Screenshots with Web Bundles</title>
      <dc:creator>Christian Aysner</dc:creator>
      <pubDate>Thu, 20 Aug 2020 14:49:48 +0000</pubDate>
      <link>https://dev.to/aysner/interactive-screenshots-with-web-bundles-4cf</link>
      <guid>https://dev.to/aysner/interactive-screenshots-with-web-bundles-4cf</guid>
      <description>&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;WARNING: This article is about technology that is highly experimental and still in development - things could change, break or never get released to the public. (20. Aug. 2020)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Screenshots are useful for giving someone a quick impression of a website or web app. Unfortunately, screenshots are not enough to communicate the feel and usability of a website.&lt;/p&gt;

&lt;p&gt;Many things cannot be shown on screenshots, especially when it comes to interactive elements like an image gallery, a web form or animations.&lt;br&gt;
One possible way out of this is to capture a video of the website, but usually that can't cover everything and you may end up with multiple videos.&lt;/p&gt;

&lt;p&gt;But there is a solution! 🎉 Google has introduced a new technology in 2019 called "Web Bundles".&lt;/p&gt;

&lt;p&gt;A Web Bundle stores all HTML, CSS, JavaScript and other assets in a single shareable file. This makes it possible to share a Website just like a PDF or Word document, by simply sending a file.&lt;/p&gt;

&lt;p&gt;The website then works completely offline and is still interactive. (Interactive of course only as long as it doesn't need any data that is not included in the bundle - like search results or something like that)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h3fTgQCs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/117bwdoaas3bx2w3eu6z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h3fTgQCs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/117bwdoaas3bx2w3eu6z.gif" alt="Web Bundle in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is perfect to share a website which is still under development or that no longer exists. Imagine sending a file to the customer to show him the current development status or a portfolio with interactive sites instead of screenshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install the needed software
&lt;/h2&gt;

&lt;p&gt;Let's look at how we can create our first interactive screenshot. (respectively Web Bundle)&lt;br&gt;
To create it, we need an application that converts HAR files into the Web Bundle format.&lt;/p&gt;

&lt;p&gt;We will use software which is written in Go, so we have to install Go first.&lt;br&gt;
Under Arch or Manjaro Linux you can type in your terminal&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pacman -Ss go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When this is done, you can install the actual software with the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go get -u github.com/WICG/webpackage/go/bundle/cmd/...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(Yes really with the three dots at the end)&lt;/p&gt;

&lt;p&gt;The actual software contains the applications "dump-bundle", "gen-bundle" and "sign-bundle".&lt;/p&gt;

&lt;p&gt;We will only need the gen-bundle application in this article.&lt;br&gt;
With sign-bundle you could sign the Web Bundle and with the dump-bundle application you could check which data is included in a Web Bundle file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create the HAR file
&lt;/h2&gt;

&lt;p&gt;So now that we have the necessary software, next we have to tell the application which pages or data it should bundle for us.&lt;/p&gt;

&lt;p&gt;One way to do this is to use an HAR file which contains all server requests and server responses.&lt;/p&gt;

&lt;p&gt;This file can be easily created using the "Network" panel in the Chrome Developer Tools.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important: Please make sure that "Disable Cache" is enabled in the Developer Tools, otherwise you may get problems with resources that are already in the browser cache.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you have the network panel open, reload the page. You may need to scroll the page once from top to bottom to ensure that all lazy loaded files are included in the HAR file.&lt;br&gt;
It makes sense to do the whole thing in the mobile view as well.&lt;/p&gt;

&lt;p&gt;After that you can right-click on one of the server requests in the network panel and select "Save all as HAR with content".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YG9pMH5o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4hf0etntgh9s9sfszkrx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YG9pMH5o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4hf0etntgh9s9sfszkrx.gif" alt="Chrome Developer tools - save as HAR file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have an HAR file which is usually a few megabytes large, depending on the website.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hint: In the lower right corner of the Network panel you can see the size of all resources, this will be approximately the size of our Web Bundle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: Generate the Web Bundle
&lt;/h2&gt;

&lt;p&gt;To build our first "Web Bundle" we have to call the following command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;~/go/bin/gen-bundle -har urlaubsguru.har -o urlaubsguru.wbn -primaryURL "https://www.urlaubsguru.at/"&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important: The primaryURL parameter must match exactly the Request URL! For example, I forgot about the slash at the end and the Web Bundle file was no longer created.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hooray! Our first Web Bundle should be ready now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Testing in the browser
&lt;/h2&gt;

&lt;p&gt;As already explained in the beginning, Web Bundles are still highly experimental and therefore we have to activate the functionality in the Chrome browser.&lt;/p&gt;

&lt;p&gt;Enter the following line in your Chrome browser address bar.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chrome://flags/#web-bundles&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this address you can activate the feature and then you have to restart the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rRCD4-OI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7v79oixqc8bvblt77c55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rRCD4-OI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7v79oixqc8bvblt77c55.png" alt="Chrome flag settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: As soon as you change the setting, a button "Restart browser" appears, but there is another possibility to restart Chrome. One which can be useful from time to time during development. Just type &lt;code&gt;chrome:restart&lt;/code&gt; into the address bar and the browser will restart.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now it should be possible to open the Web Bundle file in the Chrome browser.&lt;br&gt;
If the website is displayed that's fine, but that doesn't mean that everything will work offline.&lt;br&gt;
Chrome will try to load resources which are not in the bundle from the original URL.&lt;br&gt;
This also means that if you click on a link that is not in the bundle, the real website will open.&lt;/p&gt;

&lt;p&gt;When you want to be sure that you have all the assets you need for the website included in the bundle, you can open the bundle after enabling the offline mode in the Chrome Developer Tools. &lt;/p&gt;

&lt;h2&gt;
  
  
  Save multiple pages.
&lt;/h2&gt;

&lt;p&gt;Now we have our first interactive screenshot. Animations for the slider works, we can test the behavior of the input fields and we can see how the images are lazy loaded when we scroll the page down.&lt;/p&gt;

&lt;p&gt;But we can go one step further and save several pages into one bundle. So it will be possible to go from the start page to a detail page for example.&lt;/p&gt;

&lt;p&gt;Unfortunately, the Chrome Developer Tools sometimes cause problems with generating HAR files for multiple pages. There is the option "preserve log" but it doesn't guarantee that all request responses will be saved in the HAR file. &lt;/p&gt;

&lt;p&gt;Therefore, I wrote a puppeteer script to fix the exported HAR file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aysner/harWebBundleFixer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Nfi5Vqg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/43ydoti7qwwgyrhc7w1t.png" alt="Screenshot of the harWebBundleFixer github page"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/aysner/harWebBundleFixer"&gt;https://github.com/aysner/harWebBundleFixer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To export a website with multiple pages, open the chrome developer tools. Clear the content of the "Network panel", activate "Preserve log", reload the website, capture all the requests and continue that for all the subpages which you would like to have included.&lt;/p&gt;

&lt;p&gt;Save the HAR file and then run the harWebBundleFixer. This script tries to download all requests that have no response content in the HAR file and changes 302 status codes to 200. (304 is the code for "Not Modified" and these responses would be ignored by the gen-bundle application)&lt;/p&gt;

&lt;p&gt;The harWebBundleFixer writes a new har file to the disk, this file can now be used to create the Web Bundle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;There is far more potential in this technology than just "interactive screenshots". In the future this could open up whole new ways of distributing websites and webapps. Maybe via USB stick or P2P network? Furthermore, websites could be pre-loaded in the background by the browser and without JavaScript running, all necessary assets would already be downloaded at the moment the user clicks on the link.&lt;/p&gt;

&lt;p&gt;To make sure that all this works safely, Web Bundles will get the possibility to be signed by the owner of a domain. Once a Web Bundle is signed it will run under the actual context of the website - so it can access all localStorage data, cookies and so on from the original domain.&lt;/p&gt;

&lt;p&gt;This would also be a great option for distributing web apps that are already working without a server.&lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.dev/web-bundles/"&gt;web.dev web bundles article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tools.ietf.org/html/draft-yasskin-wpack-bundled-exchanges-03"&gt;IETF Draft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unsplash.com/@kelli_mcclintock"&gt;Cover image created by kelli_mcclintock from unsplash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webbundle</category>
      <category>screenshots</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Reading performance data with JavaScript.</title>
      <dc:creator>Christian Aysner</dc:creator>
      <pubDate>Sun, 05 Jul 2020 14:12:57 +0000</pubDate>
      <link>https://dev.to/aysner/reading-performance-data-with-javascript-3pha</link>
      <guid>https://dev.to/aysner/reading-performance-data-with-javascript-3pha</guid>
      <description>&lt;p&gt;How fast is my website on the user device? Did my code change have any negative effect on the page loading time?&lt;/p&gt;

&lt;p&gt;These are questions which we can answer with the Navigation Timing API!&lt;/p&gt;

&lt;p&gt;With this API it is possible to read various performance data via JavaScript. We can use it to get real device performance measurements from our website visitors or for quickly checking code and website changes at developing time.&lt;/p&gt;

&lt;p&gt;We can already see the page load time in Google Analytics (Behavior &amp;gt; Site Speed), but to find out where we can optimize we usually need some more data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation Timing v1
&lt;/h2&gt;

&lt;p&gt;The first version of Navigation Timing is a well supported solution to read out performance data. You can access it data via the &lt;code&gt;performance.timing&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;index&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;navigationStart&lt;/td&gt;
&lt;td&gt;1593164606981&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unloadEventStart&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unloadEventEnd&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;redirectStart&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;redirectEnd&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fetchStart&lt;/td&gt;
&lt;td&gt;1593164606987&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domainLookupStart&lt;/td&gt;
&lt;td&gt;1593164606990&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domainLookupEnd&lt;/td&gt;
&lt;td&gt;1593164607010&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connectStart&lt;/td&gt;
&lt;td&gt;1593164607010&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connectEnd&lt;/td&gt;
&lt;td&gt;1593164607249&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secureConnectionStart&lt;/td&gt;
&lt;td&gt;1593164607199&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requestStart&lt;/td&gt;
&lt;td&gt;1593164607250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;responseStart&lt;/td&gt;
&lt;td&gt;1593164607266&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;responseEnd&lt;/td&gt;
&lt;td&gt;1593164607284&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domLoading&lt;/td&gt;
&lt;td&gt;1593164607296&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domInteractive&lt;/td&gt;
&lt;td&gt;1593164608387&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domContentLoadedEventStart&lt;/td&gt;
&lt;td&gt;1593164608587&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domContentLoadedEventEnd&lt;/td&gt;
&lt;td&gt;1593164608590&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domComplete&lt;/td&gt;
&lt;td&gt;1593164609302&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loadEventStart&lt;/td&gt;
&lt;td&gt;1593164609302&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loadEventEnd&lt;/td&gt;
&lt;td&gt;1593164609302&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All values are in the JavaScript timestamp format. (milliseconds since January 1, 1970)&lt;br&gt;&lt;br&gt;
Unfortunately, this leads to the situation that we first have to do a few calculations in order to come to meaningful output. &lt;/p&gt;

&lt;p&gt;To get something like "domComplete after 1 second" we have to subtract the navigationStart timestamp from the domComplete timestamp. Chronologically the navigationStart is the first event that gets logged.&lt;br&gt;
So we can run &lt;code&gt;domComplete - navigationStart&lt;/code&gt; and we will get 2321 as result. (1593164609302 - 1593164606981)&lt;br&gt;&lt;br&gt;
In other words the moment when document.readyState jumped to complete was after 2.3 seconds.&lt;/p&gt;

&lt;p&gt;The browser support of Navigation Timing is very good, every browser except Internet Explorer 11 supports it, but v1 is outdated and will soon be replaced by Navigation Timing v2.&lt;/p&gt;
&lt;h2&gt;
  
  
  Navigation Timing v2
&lt;/h2&gt;

&lt;p&gt;In the new version we access the data via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The values are no longer timestamps but already the milliseconds since the navigationStart event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;index&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;unloadEventStart&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unloadEventEnd&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domInteractive&lt;/td&gt;
&lt;td&gt;1405.5849999999737&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domContentLoadedEventStart&lt;/td&gt;
&lt;td&gt;1606.17000000002&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domContentLoadedEventEnd&lt;/td&gt;
&lt;td&gt;1609.4399999997222&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domComplete&lt;/td&gt;
&lt;td&gt;2320.669999999609&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loadEventStart&lt;/td&gt;
&lt;td&gt;2320.7649999999376&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loadEventEnd&lt;/td&gt;
&lt;td&gt;2320.7800000000134&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;type&lt;/td&gt;
&lt;td&gt;"navigate"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;redirectCount&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;initiatorType&lt;/td&gt;
&lt;td&gt;"navigation"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nextHopProtocol&lt;/td&gt;
&lt;td&gt;"http/1.1"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;workerStart&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;redirectStart&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;redirectEnd&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fetchStart&lt;/td&gt;
&lt;td&gt;5.5749999996805855&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domainLookupStart&lt;/td&gt;
&lt;td&gt;8.484999999836873&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domainLookupEnd&lt;/td&gt;
&lt;td&gt;29.160000000047148&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connectStart&lt;/td&gt;
&lt;td&gt;29.160000000047148&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connectEnd&lt;/td&gt;
&lt;td&gt;268.12500000005457&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secureConnectionStart&lt;/td&gt;
&lt;td&gt;217.72499999997308&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requestStart&lt;/td&gt;
&lt;td&gt;268.50500000000466&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;responseStart&lt;/td&gt;
&lt;td&gt;284.6549999999297&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;responseEnd&lt;/td&gt;
&lt;td&gt;302.77499999965585&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;transferSize&lt;/td&gt;
&lt;td&gt;29889&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;encodedBodySize&lt;/td&gt;
&lt;td&gt;29347&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decodedBodySize&lt;/td&gt;
&lt;td&gt;134069&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;serverTimingname&lt;/td&gt;
&lt;td&gt;"&lt;a href="https://example.com/"&gt;https://example.com/&lt;/a&gt;"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;entryType&lt;/td&gt;
&lt;td&gt;"navigation"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;startTime&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;duration&lt;/td&gt;
&lt;td&gt;2320.7800000000134&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So our example code for domComplete would change to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;domComplete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The data we are getting back now is 2320.66999999999609, a value which is much more accurate than in v1 (at Firefox it is still rounded to 2321).&lt;/p&gt;

&lt;p&gt;Also noticeable is that the domLoading event is no longer available, but some new informations like "redirectCount" and "nextHopProtocol" is.&lt;/p&gt;

&lt;p&gt;Browser support is not as good here - for example in Safari we still have to work with the Navigation Timing v1. &lt;/p&gt;

&lt;h2&gt;
  
  
  Significant events
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;navigationStart&lt;/td&gt;
&lt;td&gt;User has entered or accessed a URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fetchStart&lt;/td&gt;
&lt;td&gt;Browser is ready to load the document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domainLookupStart&lt;/td&gt;
&lt;td&gt;IP is fetched from the DNS server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domainLookupEnd&lt;/td&gt;
&lt;td&gt;IP was fetched from DNS serve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connectStart&lt;/td&gt;
&lt;td&gt;Browser establishes the connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connectEnd&lt;/td&gt;
&lt;td&gt;Connection is established&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requestStart&lt;/td&gt;
&lt;td&gt;Browser sends the HTTP request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;responseStart&lt;/td&gt;
&lt;td&gt;Server responds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;responseEnd&lt;/td&gt;
&lt;td&gt;Server response is complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domLoading&lt;/td&gt;
&lt;td&gt;Browser starts document parsing (deprecated - v1 only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domComplete&lt;/td&gt;
&lt;td&gt;Document parsed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loadEventStart&lt;/td&gt;
&lt;td&gt;document.onload JavaScript is executed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loadEventEnd&lt;/td&gt;
&lt;td&gt;document.onload JavaScript has been executed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Read out interesting data
&lt;/h2&gt;

&lt;p&gt;I usually use the following data to get a quick overview of the website speed&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server response (Time To First Byte)&lt;/li&gt;
&lt;li&gt;Time needed to download the HTML file&lt;/li&gt;
&lt;li&gt;Time until DOM interactive&lt;/li&gt;
&lt;li&gt;Time until DOM complete&lt;/li&gt;
&lt;li&gt;Load event start and duration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server response time (Time To First Byte)
&lt;/h3&gt;

&lt;p&gt;How long did it take the server to start sending the HTML document? With &lt;code&gt;responseStart - navigationStart&lt;/code&gt; we can check this.&lt;br&gt;
A high value can indicate a long running server process or a slow internet connection.&lt;/p&gt;
&lt;h3&gt;
  
  
  Time needed to download the HTML file
&lt;/h3&gt;

&lt;p&gt;This value tells us how long it has taken to download the complete HTML file. For this we use &lt;code&gt;repsonseEnd - repsonseStart&lt;/code&gt;. If the value is high there are probably problems with the internet connection or the HTML document is unusually large.&lt;/p&gt;
&lt;h3&gt;
  
  
  Time until DOM interactive
&lt;/h3&gt;

&lt;p&gt;JavaScript can block user input. For example, when we have complex and time-consuming JavaScript in the HEAD block, this time can become correspondingly long. So the number that we get is the time that the user may already have seen something on screen but was not able to do anything on the website.&lt;/p&gt;
&lt;h3&gt;
  
  
  Load event start and duration
&lt;/h3&gt;

&lt;p&gt;One of the most important JavaScript events is the document load event. When it is executed depends on a number of different factors.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;loadEventStart&lt;/code&gt; we can see when the browser was ready to start the document.onload JavaScript, but even more interesting is that with the help of &lt;code&gt;loadEventStop&lt;/code&gt; we can calculate how long it took to complete.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;loadEventEnd - loadEventStart&lt;/code&gt; is the full runtime of all the JavaScript code that is added with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Building a snippet.
&lt;/h2&gt;

&lt;p&gt;We can now write a helper script which shows us all this data in the developer console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;consoleOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server response (TTFB)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML download from Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOM interactive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domInteractive&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigationStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOM complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domComplete&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigationStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigationStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigationStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event duration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventStart&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;elementName&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;consoleOutput&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;padEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;elementName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ms&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Note: You could also directly write it to the console or&lt;/span&gt;
        &lt;span class="c1"&gt;// print the timings array with console.table&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;consoleOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server response (TTFB) ......... 2ms
HTML download from Server ...... 2ms
DOM interactive ................ 383ms
DOM complete ................... 1048ms
Load event start ............... 1048ms
Load event end ................. 1048ms
Load event duration ............ 0ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This gives us a good first insight into the performance of a website.&lt;/p&gt;

&lt;p&gt;If we want to access these values in a production environment we have to rewrite it a bit because otherwise it is possible that values like &lt;code&gt;responseEnd&lt;/code&gt; and &lt;code&gt;loadEventEnd&lt;/code&gt; would not be present when the code runs.&lt;/p&gt;

&lt;p&gt;We can prevent this by inserting the code into the following function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;measureTimings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Insert code for performance measurement here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;measureTimings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readystatechange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;measureTimings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This code ensures that we do not access the data until all values are available. Unfortunately neither the "onload" nor the "readystatechange" event has the value of &lt;code&gt;loadEventEnd&lt;/code&gt; set, so we have to do the trick with setTimeout. This runs the code asynchronously at a later time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problems at development
&lt;/h3&gt;

&lt;p&gt;These data are fine, but if we call the snippet on the system where we are currently programming, we will face the problem that the server response time can be very variable. For example, if the development server takes 3 seconds longer on the first call because the cache is still empty, the load event will also be delayed by 3 seconds.&lt;/p&gt;

&lt;p&gt;To filter that out we can switch from using &lt;code&gt;navigationStart&lt;/code&gt; to the &lt;code&gt;responseEnd&lt;/code&gt; event. This gives use the time passed since the HTML file was completely downloaded and should be a much more constant value - regardless of how long server side PHP/JavaScript/.Net was running.&lt;/p&gt;

&lt;p&gt;So our array changes to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server response (TTFB)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML download from Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOM interactive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domInteractive&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOM complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domComplete&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event duration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventStart&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The script was very handy when I worked at Restplatzbörse. At that company we had a slightly slower development system which took different amounts of time to deliver the HTML document depending on whether the PHP cache was already warmed up and on some other factors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the snippet to the Chrome Developer Tools
&lt;/h3&gt;

&lt;p&gt;To quickly access the script we can add it to the Chrome Developer Tools.&lt;br&gt;
Chrome is able to save JavaScript snippets for a long time.&lt;/p&gt;

&lt;p&gt;These snippets are an extremely powerful feature of the Developer Tools. We can access all the functions and variables that we also have in the developer console.&lt;/p&gt;

&lt;p&gt;So a snippet like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;would copy the HTML source of the element, wich is currently selected in the "Elements" tab, to the clipboard.&lt;/p&gt;

&lt;p&gt;We can add our snippet under "Sources &amp;gt; Snippets &amp;gt; New snippet"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9PvzTLvE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qzxq7uy227avzr6wlk6i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9PvzTLvE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qzxq7uy227avzr6wlk6i.gif" alt="Adding snippets in Chrome Developer Tools"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;consoleOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server response (TTFB)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTML download from Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOM interactive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domInteractive&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOM complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domComplete&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventStart&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Load event duration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadEventStart&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;consoleOutput&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;padEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ms&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;consoleOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can now access it here when needed, but even faster is pressing CTRL + P in the Chrome Developer Tools and entering a callsign. This displays a list of all snippets - no matter which tab you are currently in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--plCPMKID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2odzsel2m2wdho8vqqf9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--plCPMKID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2odzsel2m2wdho8vqqf9.gif" alt="Quick access to snippets in Developer Tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigation_timing_API"&gt;Mozilla Developers - Navigation Timing API v1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/navigation-timing-2/"&gt;W3C - Navigation Timing API v2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
