Mozilla

HTML5 Articles

Sort by:

View:

  1. There is no simple solution for local storage

    TL;DR: we have to stop advocating localStorage as a great opportunity for storing data as it performs badly. Sadly enough the alternatives are not nearly as supported or simple to implement.

    When it comes to web development you will always encounter things that sound too good to be true. Sometimes they are good, and all that stops us from using them is our notion of being conspicuous about *everything* as developers. In a lot of cases, however, they really are not as good as they seem but we only find out after using them for a while that we are actually “doing it wrong”.

    One such case is local storage. There is a storage specification (falsely attributed to HTML5 in a lot of examples) with an incredibly simple API that was heralded as the cookie killer when it came out. All you have to do to store content on the user’s machine is to access the navigator.localStorage (or sessionStorage if you don’t need the data to be stored longer than the current browser session):

    localStorage.setItem( 'outofsight', 'my data' );
    console.log( localStorage.getItem( 'outofsight' ) ); // -> 'my data'

    This local storage solution has a few very tempting features for web developers:

    • It is dead simple
    • It uses strings for storage instead of complex databases (and you can store more complex data using JSON encoding)
    • It is well supported by browsers
    • It is endorsed by a lot of companies (and was heralded as amazing when iPhones came out)

    A few known issues with it are that there is no clean way to detect when you reach the limit of local storage and there is no cross-browser way to ask for more space. There are also more obscure issues around sessions and HTTPS, but that is just the tip of the iceberg.

    The main issue: terrible performance

    LocalStorage also has a lot of drawbacks that aren’t quite documented and certainly not covered as much in “HTML5 tutorials”. Especially performance oriented developers are very much against its use.

    When we covered localStorage a few weeks ago using it to store images and files in localStorage it kicked off a massive thread of comments and an even longer internal mailing list thread about the evils of localStorage. The main issues are:

    • localStorage is synchronous in nature, meaning when it loads it can block the main document from rendering
    • localStorage does file I/O meaning it writes to your hard drive, which can take long depending on what your system does (indexing, virus scanning…)
    • On a developer machine these issues can look deceptively minor as the operating system cached these requests – for an end user on the web they could mean a few seconds of waiting during which the web site stalls
    • In order to appear snappy, web browsers load the data into memory on the first request – which could mean a lot of memory use if lots of tabs do it
    • localStorage is persistent. If you don’t use a service or never visit a web site again, the data is still loaded when you start the browser

    This is covered in detail in a follow-up blog post by Taras Glek of the Mozilla performance team and also by Andrea Giammarchi of Nokia.

    In essence this means that a lot of articles saying you can use localStorage for better performance are just wrong.

    Alternatives

    Of course, browsers always offered ways to store local data, some you probably never heard of as shown by evercookie (I think my fave when it comes to the “evil genius with no real-world use” factor is the force-cached PNG image to be read out in canvas). In the internal discussions there was a massive thrust towards advocating IndexedDB for your solutions instead of localStorage. We then published an article how to store images and files in IndexedDB and found a few issues – most actually related to ease-of-use and user interaction:

    • IndexedDB is a full-fledged DB that requires all the steps a SQL DB needs to read and write data – there is no simple key/value layer like localStorage available
    • IndexedDB asks the user for permission to store data which can spook them
    • The browser support is not at all the same as localStorage, right now IndexedDB is supported in IE10, Firefox and Chrome and there are differences in their implementations
    • Safari, Opera, iOS, Opera Mobile, Android Browser favour WebSQL instead (which is yet another standard that has been officially deprecated by the W3C)

    As always when there are differences in implementation someone will come up with an abstraction layer to work around that. Parashuram Narasimhan does a great job with that – even providing a jQuery plugin. It feels wrong though that we as implementers have to use these. It is the HTML5 video debate of WebM vs. H264 all over again.

    Now what?

    There is no doubt that the real database solutions and their asynchronous nature are the better option in terms of performance. They are also more matured and don’t have the “shortcut hack” feeling of localStorage. On the other hand they are hard to use in comparison, we already have a lot of solutions out there using localStorage and asking the user to give us access to storing local files is unacceptable for some implementations in terms of UX.

    The answer is that there is no simple solution for storing data on the end users’ machines and we should stop advocating localStorage as a performance boost. What we have to find is a solution that makes everybody happy and doesn’t break the current implementations. This might prove hard to work around. Here are some ideas:

    • Build a polyfill library that overrides the localStorage API and stores the content in IndexedDB/WebSQL instead? This is dirty and doesn’t work around the issue of the user being asked for permission
    • Implement localStorage in an asynchronous fashion in browsers – actively disregarding the spec? (this could set a dangerous precedent though)
    • Change the localStorage spec to store asynchronously instead of synchronously? We could also extend it to have a proper getStorageSpace interface and allow for native JSON support
    • Define a new standard that allows browser vendors to map the new API to the existing supported API that matches the best for the use case?

    We need to fix this as it doesn’t make sense to store things locally and sacrifice performance at the same time. This is a great example of how new web standards give us much more power but also make us face issues we didn’t have to deal with before. With more access to the OS, we also have to tread more carefully.

  2. Presentation: HTML5 and friends at Mobile World Congress 2012

    As part of the WIP Jam at the Mobile World Congress in Barcelona, Spain we were asked to give and introduction to HTML5 so we sent Chris Heilmann and Joe Stagner to set the record straight in terms of what HTML5 is and what you can do with it.

    The presentation was on the last day of the conference and both of us had spent long hours explaining all Mozilla technologies on our booth and really not enough hours sleeping in between. In addition to that the room was a boiling 30 degrees and packed with lots of developers. It seems we did a good job regardless as nobody left and we had a lot of great questions afterwards.

    The slides (well, the HTML5 document) are available for you to see:

    Slides for HTML5 and friends

    However, they make much more sense when you see them in context, which is why we also provide you with a screencast of the talk:

    All in all it was a lot of fun talking to people at MWC and Boot to Gecko showed that HTML5 is the future of mobile as much as it is already the present technology of choice on the desktop.

  3. Mozilla's Boot to Gecko – The Web is the Platform

    Mozilla’s Boot to Gecko (B2G) is about building a complete, standalone operating system for the open web. It aims at making web technologies the number one choice for applications on desktop and mobile, and we believe it can displace proprietary, single-vendor stacks for application development. And we have made some exciting progress that we want to share with you!

    Continued…

  4. Creating thumbnails with drag and drop and HTML5 canvas

    HTML5 Canvas is a very cool feature. Seemingly just an opportunity to paint inside the browser with a very low-level API you can use it to heavily convert and change image and video content in the document. Today, let’s take a quick look at how you can use Canvas and the FileReader API to create thumbnails from images dropped into a browser document.

    The final code is available on GitHub and you can see an online demo here. There is also a screencast available on YouTube:

    Step 1: Getting the files into the browser

    The first step to resize images in the browser is to somehow get them. For this, we can just add an element in the page and assign drag and drop event handlers to it:

    s.addEventListener( 'dragover', function ( evt ) {
      evt.preventDefault();
    }, false );
    s.addEventListener( 'drop', getfiles, false );

    Notice that we only prevent the default behaviour when we drag things over the element. This is to prevent the browser from just showing the images when we drag them in.

    The getfiles() function then does the hard work of reading all the files in and sending them on to the functions that do the resizing and image generation:

    function getfiles( ev ) {
      var files = ev.dataTransfer.files;
      if ( files.length > 0 ) {
        var i = files.length;
        while ( i-- ) {
          var file = files[ i ];
          if ( file.type.indexOf( 'image' ) === -1 ) { continue; }
          var reader = new FileReader();
          reader.readAsDataURL( file );
          reader.onload = function ( ev ) {
            var img = new Image();
            img.src = ev.target.result;
            img.onload = function() {
            imagetocanvas( this, thumbwidth, thumbheight, crop, background );
            };
          };
        }
      }
      ev.preventDefault();
    };

    The drop event gives us a property called dataTransfer which contains a list of all the files that have been dropped. We make sure that there was at least one file in the drop and then iterate over them.

    If the file type was not an image (or in other words the type property of the file does not contain the string “image”) we don’t do anything with the file and continue the loop.

    If the file is an image we instantiate a new FileReader and tell it to read the file as a Data URL. When the reader successfully loaded the file it fires its onload handler.

    In this handler we create a new image and set its src attribute to the result of the file transfer. We then send this image to the imagetocanvas() function with the parameters to resize (in the demo these come from the form):

    function imagetocanvas( img, thumbwidth, thumbheight, crop, background ) {
      c.width = thumbwidth;
      c.height = thumbheight;
      var dimensions = resize( img.width, img.height, thumbwidth, thumbheight );
      if ( crop ) {
        c.width = dimensions.w;
        c.height = dimensions.h;
        dimensions.x = 0;
        dimensions.y = 0;
      }
      if ( background !== 'transparent' ) {
        cx.fillStyle = background;
        cx.fillRect ( 0, 0, thumbwidth, thumbheight );
      }
      cx.drawImage(
        img, dimensions.x, dimensions.y, dimensions.w, dimensions.h
      );
      addtothumbslist( jpeg, quality );
    };

    This function gets the desired thumbnail size and resizes the canvas to these dimensions. This has the added benefit of wiping the canvas so that no old image data would be added to our thumbnail. We then resize the image to fit into the thumbnail using a resize() function. You can see for yourself what this one does in the source code, it just means the image gets resized to fit. The function returns an object with the width and the height of the new image and the x and y position where it should be positioned onto the canvas.

    If we don’t want the full-size thumbnail but instead crop it we resize the canvas accordingly and reset x and y to 0.

    If the user requested a background we fill the canvas with the colour. After that we put the image on the canvas with the x and y coordinates and the new width and height.

    This takes care of creating a new thumbnail on the canvas, but we haven’t got it as an image in the document yet. To this end, we call addtothumbslist():

    function addtothumbslist( jpeg, quality ) {
      var thumb = new Image(),
          url = jpeg ? c.toDataURL( 'image/jpeg' , quality ) : c.toDataURL();
      thumb.src = url;
      thumb.title = Math.round( url.length / 1000 * 100 ) / 100 + ' KB';
      o.appendChild( thumb );
    };

    This one creates a new image and checks if the users wanted a JPG or PNG image to be created. PNG images tend to be better quality but also bigger in file size. If a JPG was requested we call the canvas’ toDataURL() method with two parameters: the requested JPEG mime type and the quality of the image (ranging between 0 and 1 with 1 being best quality). If a PNG is wanted, we can just call toDataURL() without any parameters as this is the default.

    We set the src of the image to the generated url string and add a title showing the size of the image in KB (rounded to two decimals). All that is left then is to add the thumb to the output element on the page.

    That’s it, you can now drag and drop images into the browser to generate thumbnails. Right now, we can only save them one at a time (or if you have some download add-ons all at once). Would be fun to add Zip.js to the mix to offer them as a zip. I dare you! :)

    More reading:

  5. Hidden Gems of HTML5: classList

    If you are a web developer, you surely must know how handy it is to dynamically change the class attribute on an element. The benefits this technique are quite a few:

    • You leave any changes in the look and feel to the CSS
    • You avoid having to loop lots of elements as you can allow CSS to do that job for you by assigning a class on a parent element
    • You can trigger CSS transitions and avoid having to write your own animation
    • And many more…

    The issue with classes is that it is not too simple to work with because of their representation in the DOM. When you read out className you get one string and you need to split it and use regex to find if a class was used and all kind of other annoyances. This is also why it is a very common interview questions for web developers to write a function to deal with classes.

    Well, you might not be aware of it, but HTML has a very cool new way to deal with classes called classList. This makes it dead easy to add, remove, toggle and check for classes on an element – natively in your browser. You can play with it at JSFiddle:

    JSFiddle demo.

    The methods you have are all you really need:

    • element.classList.add('foo') adds the class foo to the element (if it already exists it does nothing)
    • element.classList.remove('foo') removes the class foo from the element
    • element.classList.toggle('foo') alternatively adds and removes the class foo from the element
    • element.classList.contains('foo') returns if the class is applied to the element or not
    • element.classList.toString() returns all the classes as a string (same as reading out className)

    The browser support is very good with IE being the party pooper. However, there is a polyfill by Eli Grey available for you to use.

  6. Mozilla Vision 2012: The Future of HTML5 and Web Technologies

    We are currently in Tokyo, Japan for the Mozilla Vision 2012 conference and hack day. For two days Mozilla Japan with friends from the other locations are putting up an amazing effort to encourage people to help us educate the next generation of web makers.

    Chibi and Jono showing off a platform game

    Being in Japan, all of this is of course wrapped in quite some amazing tech like voice activated robots with emotional responses, but also good old dinosaurs you can take your tourist shots with:

    Chris Heilmann, Mozilla Godzilla and Mark Finkle

    My part of the play was to give a talk about HTML5 and how we can use it now to give our users a better web experience. The slides are available.

    There is an audio recording of the talk on archive.org:

    The demos I showed in the talk were:

    Today we are at a hack day – stay tuned for some more of the information on that soon.

  7. New features for HTML5 video playback in Firefox

    As explained in this blog post by Jared Wein of the Firefox team there are quite a few new features in Firefox when it comes to playing HTML5 video. As an Aurora user, I am most excited about the option to go full-screen, the ability to overlay video statistics and to save a snapshot of the current frame as a JPG. You can see me talking about and showing them in this short video:

    Firefox has a few features up its sleeve when it comes to HTML5 video playback you might not be aware of:

    • Firefox‘s seeking is now accurate to millisecondsmicroseconds, there is visual feedback when the video has stalled and clicking the whole video pauses and plays it
    • Firefox Beta has specialised controls when you watch video on small devices and watching HTML5 video shows a pleasing background rather than a brutal grey
    • Firefox Aurora has fullscreen, statistics overlay, saving of snapshots and controls appearing when the video ended
    • Firefox Nightly has a full-screen button, fading video controls after 2 seconds of non-interaction, no loading throbber on audio, error reporting when a video could not be loaded on the video, loop attribute support, and resizing of videos larger than the browser window when you watch them directly

    Planned features for Firefox are an overlay play button like YouTube when the video is not set to autoplay and turning off screensavers during fullscreen playback.

    Check out Jared’s post for more information.