aboutsummaryrefslogtreecommitdiffstats
path: root/docs/_posts/2016-12-16-v2.0-javascript-improvements.md
diff options
context:
space:
mode:
authorMatthew Somerville <matthew-github@dracos.co.uk>2018-09-27 14:56:52 +0100
committerMatthew Somerville <matthew-github@dracos.co.uk>2018-09-27 14:56:52 +0100
commitb3fea58c6f9a29ec5fb428d82c25e3a82ac962af (patch)
treef7b79502c8bcbc158451c205944ee8d337750f8e /docs/_posts/2016-12-16-v2.0-javascript-improvements.md
parent371927debffc6bb42d8d86a90afc715d1d837e74 (diff)
Move docs from gh-pages branch.
Diffstat (limited to 'docs/_posts/2016-12-16-v2.0-javascript-improvements.md')
-rw-r--r--docs/_posts/2016-12-16-v2.0-javascript-improvements.md123
1 files changed, 123 insertions, 0 deletions
diff --git a/docs/_posts/2016-12-16-v2.0-javascript-improvements.md b/docs/_posts/2016-12-16-v2.0-javascript-improvements.md
new file mode 100644
index 000000000..fa82fef6f
--- /dev/null
+++ b/docs/_posts/2016-12-16-v2.0-javascript-improvements.md
@@ -0,0 +1,123 @@
+---
+layout: post
+title: Version 2.0 – JavaScript performance
+author: matthew
+---
+
+The JavaScript on FixMyStreet has gradually evolved over many years (we
+launched in 2007, remember!), and while we were working on other features in
+this area (such as HTML5 History) it was a good opportunity to tidy up the
+JavaScript, making it clearer and simpler. Below I'm going to go through most
+of the steps I took, not necessarily in the order I took them, which hopefully
+might prove useful to your own websites. And there are exciting pictures at the
+end, I promise!
+
+It also let us add the Content-Security-Policy header to FixMyStreet, which is
+a method in browsers to prevent cross-site scripting (XSS), clickjacking and
+other code injection attacks by specifying the valid sources for script
+execution.
+
+## Separate scripting and styling
+
+When you report an issue on FixMyStreet on mobile, the map `/around` page is
+full-screen to make reporting easier. This was being done in JavaScript using
+some jQuery `css()` calls when setting up (or ending) the mobile view. It was
+straightforward to move this CSS to a `mobile-reporting-map` class and have the
+JavaScript do no more than add or remove this class, making both parts clearer.
+(Later during this process, I also added an `only-map` class to prevent the map
+being scrolled until it is clicked, when the rest of the form can then be
+shown.)
+
+Similarly, the JavaScript was requisitioning the desktop-width green banner to
+provide a different mobile banner; updating this to use a separate mobile
+banner made the code clearer and shorter.
+
+## Tidy up the JavaScript set up
+
+The main setting up of our JavaScript was taking place, for historical reasons,
+in two files, both confusingly called `fixmystreet.js`. Whilst in the future we
+may want to split this up into separate files more tailored to the particular
+pages where the code is used (though our main use of JavaScript is our map
+page, which already has its JavaScript separate), for now it made most sense to
+combine these in one file, and tidy up all the setup functions into a list of
+feature-based functions each called in turn on page load.
+
+We were bundling a copy of Modernizr (for media query detection) that contained
+html5shiv and yepnope. But html5shiv is only needed in old versions of Internet
+Explorer, and yepnope is only used on FixMyStreet's front page (to try and
+preload the map page JavaScript mentioned above), so I could move html5shiv
+into an IE conditional comment, and included yepnope.js only on the front page,
+reducing Modernizr to only Modernizr itself.
+
+## Move vital JavaScript as early as possible
+
+Now that I had a `mobile-reporting-map` class, I wanted this class activated
+as soon as possible as the page is loading, not only when the document had been
+parsed. There were also a couple of site-wide variables, `page` (the type of
+page, e.g. `around` or `new`), and `cobrand` (the branding of the site you're
+on). Lastly, I wanted to be able to set a class on the document if JavaScript
+was activated, so that CSS using that class would be instantly active,
+preventing a flash of style change or content.
+
+To achieve all this, I created a `header.js` file that performed the above
+three tasks, setting a class on &lt;html&gt;, setting two variables on our
+global `fixmystreet` variable, and if we're on a small width (using Modernizr)
+and perhaps a map page, setting the appropriate classes. I then minimized and
+inlined this script in the header of each page, so that we don't have to wait
+for any external script to load.
+
+## Move JavaScript to the end of the HTML, remove inline JavaScript
+
+As [recommended by Jake Archibald
+here](https://www.html5rocks.com/en/tutorials/speed/script-loading/), I moved
+all the JavaScript to the end of the HTML. To make this easier, and also to
+make adding a Content-Security-Policy header easier, I removed all the inline
+JavaScript from FixMyStreet (I didn't think FixMyStreet had that much inline
+JavaScript, and it didn't, but it still had more than I had remembered!). This
+was most commonly being used to set up some JavaScript variables with
+server-side data, so the easiest way to replace this was to place this data as
+attributes on HTML elements (preferably semantically related elements), and set
+up the JavaScript variables in an external script.
+
+## Minify JavaScript
+
+I didn't want to make this mandatory, as we have done with SCSS/CSS, but I
+wanted the option available, so I added an option to our templating code that
+means it will prefer an .auto.min.js file in preference to a .js file of the
+same basename. This lets you compile your JavaScript in a deploy process, for
+example, should you wish to. We do this with the standard Closure Compiler from
+Google; I haven't yet been brave enough to try and check/get the JavaScript
+working with the advanced option of the Closure Compiler :)
+
+## Activate Content-Security-Policy
+
+The Content-Security-Policy header lets you specify domains from which
+JavaScript will run, plus lets you choose to run inline JavaScript either en
+masse or only if you provide a specific unique nonce ID in the &lt;script&gt;
+tag that matches the same ID in the CSP header. That latter option is how we
+kept our inline header JavaScript running without having to externalise it
+again.
+
+"nonce" was only added in the second version of the CSP spec, so you may note
+our header also specifies `unsafe-inline`. Any browser that supports version 2
+will ignore this when it sees the nonce header, but it is needed in order for
+the inline script to still run in any browser only supporting version 1.
+
+## Conclusion
+
+In Google Page Speed Insights, with manual minification of the main JS files,
+this moves the front page from 68/84 to 85/92ish (filmstrip from
+webpagetest.org, top is live site, bottom is my dev site):
+
+[![filmstrip](https://cloud.githubusercontent.com/assets/154364/17670115/58b741f6-6308-11e6-9510-82c23fbd5c35.png)](https://cloud.githubusercontent.com/assets/154364/17670115/58b741f6-6308-11e6-9510-82c23fbd5c35.png)
+
+These are requests from the US: most of the initial delay is in that initial
+download. Now here's a report page going from 58/77 to 85/86ish ("ish" because
+e.g. live site will have analytics that my dev site doesn't):
+
+[![Site visible a second quicker](https://cloud.githubusercontent.com/assets/154364/17670122/680c9a16-6308-11e6-9bfb-721272ff1f71.png)](https://cloud.githubusercontent.com/assets/154364/17670122/680c9a16-6308-11e6-9bfb-721272ff1f71.png)
+
+Due to page elements displaying more quickly than might previously have been
+expected, there are still a few things to tidy up. For example, when an element
+displays before a bit of JavaScript kicks in and makes it look slightly
+different... but hopefully nothing major.