24 Dec 2017, 14:53

Getting going with ES6 Maps

As we move into a world where modern browsers are supporting the nearly the entire ES6 specification, we have a variety of data structures to use for storing and accessing our data as native built-in primitives. In ES6 we specifically gain access to ‘keyed collections’. Keyed collections encompasses four built-ins, these are namely Set, Map, WeakSet and WeakMap. Here we’ll be talking about the Map in particular. Let’s get stuck in!

What is a Map?

A Map is a collection of key value pairs, where both key and values can be of any type. This differs from how one might have traditionally used a JavaScript Object as a map, as historically keys have been strings only (this has changed with the introduction of Symbols but that’s for a different blog post to come!).


const obj = { 1 : "one" }
typeof Object.keys(obj)[0] // Evaluates to string

So with Maps, we have the additional power of using anything we like as our key, even a HTMLElement, which is powerful because it makes it easy to associate data with a DOM node. To illustrate the use of none String keys, we can see this example using booleans:


const map = new Map([
    [true, "Things that are true"],
    [false, "Things that are false"]
]);

Here true and false are the respective keys in our collection. So how does JavaScript know how to evaluate something as matching a Map key? Well the approach is very similar to the === (identity) operator, with the minor caveat that NaN (Not a Number) equates to NaN even though NaN !== NaN.

How do we use it?

Maps provide a set of methods for interfacing with them such as get, set and delete. These methods provide a clean inteface for interacting with our data. Let’s see how that works in practice:


const one = "one";
const map = new Map([ ["one", 1] ])
map.get("one"); // Returns 1
map.get(one); // Returns 1

map.set("two", 2);
// Returns the Map with "two" set to 2

map.delete("one");
// Returns true if successful or false if unsuccessful

map.clear();
// Returns undefined

As you can see there’s a little gotcha here, get, set and delete all have different return behaviours. Get will return the associated value, set will return the Map itself, and delete will return a boolean, and clear returns undefined. Something to keep in mind!

We also have some really nice convenience methods to help us interface. For example, has which will tell us if a key exists in a Map:


const map = new Map([ ["one", 1], ["two", 2] ]);
map.has("two") // true

This is very useful for quickly checking the existence of key in a Map. What about if we want to iterate over a Maps contents? Firstly there are a few convenient methods in this regard: values, entries and forEach. Let’s look at values and keys. Both these methods return an iterable. Iterables are a little outside the scope of this post, but think of them as JavaScript variables that have defined behaviours for what happens when you iterate over them (for example using for…of):


// Entries
const map = new Map([ ["one", 1], ["two", 2] ]);

for (const num of map.entries()) {
    console.log(num); 
    // Logs ["one", 1]
    // Then ["two", 2]
}

// Values
for (const key of map.values()) {
    console.log(key);
    // Logs 1
    // Then 2
}

Alongside this we have the forEach method that some of you may be familiar with from the Array method of the same name. It behaves like so:


const map = new Map([ ["one", 1], ["two", 2] ]);
map.forEach((key, value) => console.log(key, value));
// Logs ["one", 1]
// Then ["two", 2]

Lastly we can quickly and conveniently get the size of a map with the size method. It is fairly straight forward and operates like so:

const pets = Map([ ["dog", "woof!"], ["cat", "meow"], ["goldfish", "bop bop bop"] ]);
animals.size(); // Evaluates to 3

Which would have been less elegant using an Object:

var pets = {"dog", "woof!", "cat": "meow", "goldfish": "bop bop bop"};
Object.keys(pets).length;

Why use a Map?

Simply put, the Map is more idiomatic and effective primitive than an Object for use as a mutable key-value pair data structure. We have the added flexibility of using anything we please as our keys, alongside a clear set of a powerful methods that we have shown above. These methods allow us to be more semantic and straightforward about interacting and manipulating our data. It also avoids some of the potential pitfalls of the Object, for example, because of the interface of a Map, we can avoid the possibility of colliding with default Object properties such as toString or hasOwnProperty. This is obviously an edge case but provides us with extra reassurance:


// With a traditional Object
const obj = {};
obj["toString"] = "woops";
console.log(obj.toString);
// Evaluates to woops

// With a Map
const map = Map();
map.get("toString") // undefined
map.set("toString", "This is fine");

Overall it expresses a lot of cool properties that suite it better to behaviours we wanted from using an Object as a Map previously. Happy Mapping!

23 Aug 2017, 19:28

Low Hanging PWA Fruit: Manifest Files and Service Worker Precache

It would be a fair assessment to state that Progressive Web Apps (often abbreviated to PWA) are a popular buzzword in the web development and JavaScript communities at the moment. We have also seen further excitement recently, generated by Apple’s move to start supporting development for PWA features within Safari.

Many great posts and documentation exist around explaining the full case for PWAs and how they benefit users. For a solid introduction to Progressive Web Apps check out Google’s page on the matter. In addition, if you’re looking for something tangible there’s also lots of great examples of PWAs that you can explore in more detail at pwa.rocks.

Perhaps now more than ever it’s time to start exploring how you can start looking into aligning your app or site with PWA functionality. This post will be more practical than theoretical, but to give some context I will list out some of the core concepts of what makes a PWA for those of you unfamiliar:

  • Pages are responsive: The app’s content fits in a sensible way on a tablets, mobiles etc
  • Site is served over HTTPS: assets and resources are served using encryption over the wire
  • The entry page loads offline for users
  • The app uses a manifest.json file to allow adding the app to a users homescreen
  • Load times are reasonable even on slower connections like 3G
  • Each page has its own unique URL
  • The site works across modern browsers

Here we will just focus in on two of these concepts and how to integrate them, namely adding a manifest.json file and adding a Service Worker. Subsequently, we can also use the Service Worker to allow the initial page to work offline by using a precache.

Adding a Manifest File

A manifest file provides a few additional weapons to the web app arsenal for developers. The file itself is fairly plain; a JSON file that contains configuration and metadata for various aspects of the application.

However what the manifest file allows for is a whole host of interesting benefits for those looking to give a more ‘app like’ experience:

  • Allowing users to add a web app to their home screen
  • Giving that home screen button a unique name and icon
  • Giving a splash screen to the app when the user loads it
  • Being able to add a theme colour for the web app
  • Define if the app should load ‘standalone’ or in a web browser

Now we’ve gone through why you would add a manifest.json file, let’s take a look at an example config:


{
  "name": "SomeCoolApp",
  "short_name": "SomeCoolApp",
  "start_url": ".",
  "display": "standalone",
  "background_color": "#111111",
  "description": "An app for doing cool things",
   "icons": [{
    "src": "images/manifest/48.png",
    "sizes": "48x48",
    "type": "image/png"
  }, {
    "src": "images/manifest/72.png",
    "sizes": "72x72",
    "type": "image/png"
  }, {
    "src": "images/manifest/96.png",
    "sizes": "96x96",
    "type": "image/png"
  }, {
    "src": "images/manifest/144.png",
    "sizes": "144x144",
    "type": "image/png"
  }, {
    "src": "images/manifest/168.png",
    "sizes": "168x168",
    "type": "image/png"
  }, {
    "src": "images/manifest/homescreen192.png",
    "sizes": "192x192",
    "type": "image/png"
  }],
  "related_applications": [{
    "platform": "web"
  }, {
    "platform": "play",
    "url": "https://play.google.com/store/apps/details?id=SomeCoolApp"
  }]
}

You can see that we provide a name and short_name which give a name and an abbreviated name for the app when space is a premium. start_url expresses where the kick off URL for the application when the user starts the app. For example, it could be their profile page or the home page.

As mentioned, we can decide if the app should load in a web browser or have a more native experience. This is realised through the display property. We can provide standalone to give a more native feel as it hides away browser features like the URL bar (this may be a negative feature depending on your feelings!). There are other options for this such as fullscreen which hides OS UI features, and also minimal-ui which take a middle ground on the matter.

The background_color property allows for us to express a colour that will occur before the app has fully loaded into its container. icons provide a way to give icons for our applications at different device sizes. For a full list of options check out MDN’s page on the matter.

You could write the manifest file manually (see Google’s instructions here). However, if you want a more streamlined experience you could use a generator instead. One generator I found user-friendly and straight forward is the app-manifest app on Firebase, which you can use to generate a zip file which can be unloaded into your web app directory.

Lastly we can include it in our home page’s HTML as so:

<link rel="manifest" href="/manifest.json">

Adding a Service Worker

The Service Worker is a slightly more complex concept than the manifest file. In essence Service Workers are web workers that allow code to run outside of the web page context within the browser. The Service Worker also in some ways acts as a network proxy, allowing the developer to change the behaviour of network requests (i.e. to read from a cache rather than a remote resource). In principle, these two concepts allow for a host of new opportunities for web apps, including push notifications, offline interactivity and background sync (sending server requests upon resumption of connectivity).

Before we dive in any deeper, it’s important to note that Service Workers require HTTPS to operate, so you will need to have that setup on your site before continuing (checkout Let’s Encrypt if you’re in need of an SSL certificate).

Although we could dig in deep into the Service Worker internals and life cycle, I would rather focus on getting us up and running with a Service Worker. Others have done a great job explaining Service Workers in depth, for example see the Google primer on Service Worker or check out Phil Nash’s great introductory video here. Instead we will focus on one example Service Worker template that allows us to get a registered Service Worker into our page quickly.

An easy to leverage Service Worker template is provided by sw-precache. Using a precache allows you to serve resources from the Service Worker cache, rather than having to wait for it to come over the wire which improves the performance of the app. It also allows us to get resources whilst offline. sw-precache itself is a tool that allows for the automatic code generation of a Service Worker for caching assets that we would normally receive over the network (images, css etc). As sw-precache requires a preprocessing step to generate the Service Worker we need to leverage a task runner. Here we can use gulp, a JavaScript task runner to create a task to generate the list of resources we want to precache. Here’s some example code with comments:


    // Register the service-worker task with Gulp
    gulp.task('service-worker', function(callback) {

        // Get hold of the sw-precache module
        var swPrecache = require('sw-precache');
        var rootDir = '../public/';
        var serviceWorkerName = "precache"; 
        var fileName = `${rootDir}${serviceWorkerName}.js`;

        // Write the precache Service Worker
        swPrecache.write(fileName, {

            // An array of file paths to precache (we use globbing)
            staticFileGlobs: [
                rootDir + 'index.html',
                rootDir + 'templates/.{html}',
                rootDir + 'js/**/*.{js}',
                rootDir + 'css/**/*.{css}',
                rootDir + 'fonts/**/*.{svg,eot,ttf,woff,woff2}',
                rootDir + 'icons/**/*.{svg}',
                rootDir + 'images/**/*.{png,jpg}',
            ],
            // We remove the prefix and can replace it with another
            // Here `../public/` becomes `/` for example
            stripPrexix: rootDir,
            replacePrefix: "/"

        }, callback);

    });

Running gulp service-worker will generate our Service Worker precache file called precache.js. We can now register this into our app using the code expressed below:

    // Check to make sure the Service Worker feature exists
    if ("serviceWorker" in navigator) {
        // On page load, call the Service Worker registration
        window.addEventListener('load', registerSW);
    }

    // The actual registration code
    var registerSW = function() {

        // The location of our generated pre-cache Service Worker
        var swPath = "/precache.js";
        navigator.serviceWorker.register(swPath)
            .then(registrationSuccess, registrationFailed);

    }

    var registrationFailed = function(error) {
        // Do something if there is an error
    }

    var registrationSuccess = function(registration) {
        // Do something if everything is successful
    }

Once the Service Worker is registered the user will make use of the cache when making network requests for cached assets.

Conclusion

Here we have shown how you could add a manifest and a Service Worker to your web app without complicating your application code too much. If you already have gulp in your build process integrating sw-precache shouldn’t be too much extra effort to get up and running. Similarly using the manifest.json app shown above we can simply generate a working manifest file for our application.

Hopefully, this article has begun to pave the way for further PWA features in your applications. Support is gradually getting there so the reasons to avoid the potential benefits are decreasing. If you are interested in auditing your app to see how much it adheres to Google’s PWA checklist, check out a Chrome extension called Lighthouse. Lighthouse will score your app depending on how well it compares a set of pre-defined criteria.

25 Jun 2017, 14:59

Icon Font to SVG Icons: A worked example

There has been an on going discussion about wether Icon Fonts or SVG Icons are ‘better’ for usage on the web. This css-tricks article outlines the introductory differences between the two nicely. The author concludes that “If you can go IE 9+ / Android 3+, inline SVG is better at pretty much everything than icon fonts”. This view has been debated somewhat, with Tyler Sticka laying down a solid case for moving away from Icon Fonts and Ben Frain making a succinct rebuttal. The general consensus within the web development community appears to have been to move over to SVGs however.

One notable benefit I saw for moving to inline SVG was another opportunity to prevent unnecessary render blocking; the process by which content such as CSS/JS/Fonts slow down the rendering path for a page, preventing meaninful content getting to the screen whilst they load.

With this in mind I decided to use my own blog as a way to explore how to make this transition and critically examine any benefits (or problems) it might hold. This post will lay out my approach for swapping out my blogs icon fonts (on the left hand panel, or a the top if you’re on mobile!).

Steps

  1. Remove the icon font CSS

    In this case we were using Font Awesome. Font Awesome is a great resource, and I really admire what they’ve done. Of course however, this post is about removing icon fonts, so out it must go!

        <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
    
  2. Remove the icons themselves

    Here we remove the icons where necessary. In my case I was using 5 icons from an icon font within the sidebar. We simply delete the relevant icons from the HTML, for example the Twitter icon:

        <li class="nav-item">
            <a class="pure-button" href="https://twitter.com/{{ . }}">
                <i class="fa fa-twitter"></i> 
                twitter
            </a>
        </li>
    
  3. Grab the SVG Icons you need

    For the SVG Icons I am choosing to use edent’s SuperTinySocialIcons. These are very efficient SVG icons, covering the majority of your favourite companies/logos. This won’t cover none-social icons (arrows etc) that you might need, in my case I got the cube icon from the The Noun Project and ran it through Jake Archibald’s SVG optimiser. For a more robust approach checkout Sara Soueidan’s superb blog post about using fontello-svg to take Fontello (an aggregator for web fonts) fonts and convert them down to SVG.

  4. Inline the SVG Icons

    You might choose to link out to an external SVG asset, however for performance reasons I have inlined the SVG right into the sidebar, for example:

        <a class="pure-button" href="https://twitter.com/{{ . }}">
        twitter
        <svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
            <rect fill="#1da1f3" height="512" rx="15%" width="512"/>
            <path d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z" fill="#fff"/>
        </svg>
        </a>
    
  5. Making necessary edits

    We need to do a little bit of extra work to take the coloured icons and convert them to monochrome. Taking the Twitter icon for example, we can get rid of the rectangle entirely to remove the light blue box:

        <svg class="svg-icons" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
            <path d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z" fill="#fff"/>
        </svg>
    
  6. Scale them correctly

    Using a CSS class we can use vertical-align: middle to align the icons vertically and set the width to be in fitting with the adjacent text (16px). We could also just use direct inline CSS if desired.

Outcomes

By default Font Awesome CSS was 5.83Kb over the wire (23.18Kb uncompressed), plus the actual font itself 55.45kb (WOFF2 is compressed). That’s 61.28kb in total, plus two network requests. Using Fontello, I was able to get the font down to 3.17kb (!) by removing all the unused icons. So in total, at it’s most optimised we get down to 9.0kb compressed and two network requests (if uncached).

Inlining the SVGs in the sidebar for the index page takes the HTML page weight up from the original 8.85Kb (compressed) to 9.68Kb (compressed) increasing it by 0.83kb compressed. Using icon fonts in this case eliminated two network requests for render blocking content, and removed 8kb in page weight in the best case and over 60kb in the worst case scenario (using the default Font Awesome CDN CSS link). Lastly, I would say even if you don’t want to make the switch, at least investigate condensing your icon font usage usage down to the icons you use explicitly, using a tool like Fontello.

Things to note:

  • I didn’t do any scientific experiments on actual page load times (boooo I hear you shout), however there has been some evidence to suggest there are some marginal losses in rendering using SVGs.
  • IE9+ for support for SVGs (hopefully no longer an issue for 99% of developers)
  • Similarly older versions of Android lack support (2.3)