Chapter 3. Understanding the Gravity of HTML5

Before we dive into the game that we'll build in this chapter, we will examine why writing applications in HTML and JavaScript can be difficult when deploying in multiple different browsers. We will focus on simple and practical solutions to these problems, especially with regards to HTML5 and the latest APIs used today.

The game we will build in this chapter will be a basic jelly wobbly gravity game. It will make use of HTML5's new API for vector graphics, native audio manipulation, and drag-and-drop. As the backbone of the rendering system for this game, we will use the old JavaScript timer, which, as we will see, is not at all appropriate for games such as this one where we need multiple updates per second. Thankfully, modern browsers have solved this issue, and taken into account the need we have for highly efficient rendering engines. However, we won't be discussing this new feature until the next game. Just for completion, this new feature is known as requestAnimationFrame.

Browser compatibility

Anyone who has done any web development at all has quickly developed a very deep, profound, and thorough hatred towards the way different browsers interpret and render the same code. However, if we dig a bit deeper into this phenomena, and look for the root cause of these discrepancies, it will surprise some people to realize that the problem is not what it seems. While finding the cause for rendering differences is easy, for example, some browsers define the box model differently, finding the cause for differences in code may not be so clear. Surprisingly, some developers seem to despise the JavaScript language because some code runs differently in some browsers. However, the truth of the matter is that JavaScript is actually quite portable, and its API is quite stable and consistent.

Believe it or not, most of these headaches are caused by the DOM API, and not JavaScript itself. Some browsers register DOM-related events one way, while other browsers don't acknowledge that method, and instead use their own variation for it. The same goes for manipulating DOM elements and subtrees.

For example, one way to remove a node from the DOM is to call the remove method on the node itself. However, as of this writing, only a very limited handful of browsers expose this functionality. Most commonly, browsers allow us to remove a node from a DOM tree by calling the removeChild method on a parent node, passing a reference to the child node to be removed from the parent.

The key point to be made here is this; JavaScript itself is very consistent across browsers, but the way that browsers allow us to programmatically interact with the DOM, although this is most commonly done via JavaScript, may vary from browser to browser. While none of this is news to anybody, and certainly is not unique to HTML5, it is still important to remember that the main tool we have for programming the web platform, that is, JavaScript, is a very powerful and consistent one. The problem that we need to keep in mind is the DOM API (as well as CSS, although this particular issue is becoming less and less of an issue, as browsers are beginning to agree on common standards related to that).

Supporting different browsers

There are different approaches that we can take when developing an HTML5 application in order to ensure that the code runs the same in different browsers, and that the design is rendered the same as well. Some of these practices are painful and tedious, others are unreliable, and others are simply good enough. Unfortunately, as long as there are so many browser differences as there are today, there will never be one single silver bullet that completely makes the problem disappear.

The two main goals when it comes to writing code that runs practically identical in different browsers are; write as little unique code to each browser as possible, and write code that degrades gracefully. It is one thing to specifically target a couple of unique features specific to a particular browser, but it is a completely different issue to maintain two or more separate code bases. Remember this, that the best code you can possibly write, both in terms of efficient execution and security, is the code that you never have to write at all. The more code you write, the more subject to errors and faults your code will be. Thus, avoid writing too much code that does the same thing as other code you're also writing, but writing it uniquely for a different browser.

While being a perfectionist can be a great attribute, we must be realistic that we won't achieve perfection any time soon. Not only that, but in most cases (certainly in all cases where a video game is involved) we don't need to write software that is anywhere near perfect. At the end of the day, whether you agree with it or not, the goal of software development is to produce software that is good enough. As long as the program solves the problem for which it was written, and does so in a reasonable fashion, then for all practical purposes, we can say that the software is good.

With that introduction behind us, keep those two principles in mind as you develop HTML5 applications, including games, aimed at reaching hundreds of millions of people world wide. True, there are some browser-specific functionalities that may make a game unplayable, or at least make the user experience significantly different, that the final result might not be desirable. But, pay close attention to what you're really trying to accomplish, so as to discern what browser differences are good enough. It may very well be that a feature targeted to a particular browser is used by so few users that there is no cost benefit to the feature. What we never want to do, however, is to deploy an unusable product.

HTML5 libraries and frameworks

In our quest for multiple browser support in cost efficient ways, we can find comfort in knowing that we're not alone in this struggle. Today, there are so many open source projects aimed at solving this same problem of browser compatibility that we can possibly play the alphabet game, where we name a different HTML5 library or framework for each letter of the alphabet.

There are normally two reasons for the existence of such tools, namely to abstract away browser differences, and to speed up development. While most abstractions provided by today's JavaScript tools attempt to provide the client with a single interface that unifies browser discrepancies, a lot of these libraries also provide functionality that simply speed development time and effort.

jQuery

By far, the most popular JavaScript library is one called jQuery. If you haven't heard of jQuery before, chances are that you just woke up from a very deep and profound hibernation, while your body traveled through distant galaxies. Some of the main benefits for using jQuery includes a very powerful DOM query and manipulation engine, a very simple, unified XHR (XML HTTP Request also known as Ajax) interface, and the ability to extend it through a well defined plugin interface.

One example of how using a JavaScript library, particularly jQuery, can save you development time and effort is trying to make an asynchronous request to your server. Without jQuery, there is a bit of boilerplate code that we'd need to write so that different browsers all behave the same. The code is as follows:

var xhr = null;

// Attempt to create the xhr object the popular way
try {
  xhr = new XMLHttpRequest();
}
// If the browser doesn't support that construct, try a different one
catch (e) {
  try {
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
  }
  // If it still doesn't support the previous 2 xhr constructs, just give up
  catch (e) {
    throw new Error("This browser doesn't support AJAX");
  }

// If we made it this far, then the xhr object is set, and the rest
// of the API is identical independent of which version we ended up with
xhr.open("GET", "//www.some-website.com", true);
xhr.onreadystatechange = function(response) {
  // Process response
  // (...)
};

xhr.send();

Now, in contrast, that exact functionality can be achieved with the following code using jQuery:

$.ajax({
  type: "GET",
  url: "//www.some-website.com",
  async: true,  /* This parameter is optional, as its default value is true */
  complete: function(response) {
    // Process response
    // (…)
  }
});

One of the awesome things about jQuery's XHR functionality is that it is highly flexible. At a bare minimum, we can achieve the same behavior as in the previous code, in a completely cross-browser fashion, as shown in the following code:

$.get("//www.some-website.com", function(response) {
  // Process response
  // (…)
});

In conclusion, much can be done with very little effort, time, and code, with jQuery. All of this also comes with the added benefit that the library is developed by a very dedicated team, with a very involved and active community behind it. For more information about jQuery, check out the official website at http://www.jquery.com.

Google Web Toolkit

Another popular and extremely powerful JavaScript tool is Google Web Toolkit (GWT). First of all, GWT is not a mere library that provides a handful of abstractions on top of JavaScript, but rather a full blown development toolkit that uses the Java language (which itself comes with all of its benefits), then compiles and translates the Java code into highly optimized, browser-specific JavaScript code.

It is silly to try to compare jQuery with GWT, as each solve different problems, and take a completely different look at web development. However, it is worth saying that while jQuery is a great tool that is currently found in the toolbox of nearly every web developer today, it is not intended for, nor is it a very good fit for actual game development. Google Web Toolkit, on the other hand, while not the most appropriate tool for small, trivial HTML and JavaScript projects, lends itself very well to game development. In fact, the popular game Angry Birds used Google Web Toolkit in the development of the Google Chrome version of the game.

In conclusion, while GWT is enough a subject to occupy its own book, it is a great tool to consider when you take on your next large web development project, where one of the goals is to provide multiple browser support to your application. For more information about Google Web Toolkit, check out the official website at https://developers.google.com/web-toolkit/.

Supporting browsers with limited HTML5 features

As was previously mentioned, none of the browser-caused development headaches mentioned above are new with, or specific to, HTML5. However, it is important to know that this same problem has not gone away with HTML5 (yet). Furthermore, HTML5 brings with it a whole new level of cross-browser nightmares. For instance, while most HTML5 related APIs are well defined in a documented specification, there are also many APIs that are currently in an experimental stage (for a discussion on experimental APIs and vendor prefixes, refer back to the online chapter, Setting up the Environment, and Chapter 2, HTML5 Typography, where the topic is more thoroughly discussed). On top of that, there are also browsers that don't yet support some HTML5 features, or currently offer limited support, or worse yet, they provide support through a different interface than other browsers.

Again, as web developers we must always have the user at the top of the list of priorities when creating a new application. Since the problem of browser compatibility is still among us, some people feel that HTML5 is still a thing for the future, and the usefulness of its new features remain yet to be seen. The rest of this section will describe ways that we can use HTML5 today without having to worry about less desirable browsers, and yet provide a functional application to users using such browsers.

Gracefully degrade

If you pay close attention to the previous code snippet where we attempted to create an XHR object that works in many different browsers, you will notice that the code deliberately halts execution if the browser executing the code doesn't support one of the two options the code searched for. That is a great example of what we should not do, if at all possible. Whenever a specific feature is not available to a certain browser, the first option should be to provide an alternative construct, even if this alternative method doesn't quite provide the same behavior. We should do all that we can to at least provide a functional experience in the worst case scenario, where the browser has zero support for what we're trying to accomplish.

For example, HTML5 provides a new storage mechanism that's similar to a cookie (in other words, a simple key-value pair storage), but with the main difference being that this storage mechanism stores the data completely in the client, and that data is never sent back and forth to the server as part of the HTTP request. While the specifics of what this storage system is and how it works will be covered later in the book, we can summarize it by saying that this storage system (called Local Storage) stores key-value pairs, and does so through a well defined interface, and from a property of the Window object named localStorage.

localStorage.setItem("name", "Rodrigo Silveira");
localStorage.length == 1; // true
localStorage.getItem("name"); // "Rodrigo Silveira"
localStorage.removeItem("name");
localStorage.length; // == 0

One powerful application for Local Storage is to cache asynchronous requests made by the user, so that the subsequent requests can be fetched directly from the browser's local storage, thus avoiding the round trip to the server. However, if a browser doesn't support local storage, the worst case scenario in this particular case would be that the application would need to fetch a subsequent request from the server again. While not practical or efficient, this is by far a problem one should not lose sleep over, except if that means that we'll need to write lots of extra code to test for the presence of the localStorage object every time we need to use it, thus polluting the code base with many repetitive conditional statements.

A simple solution to a problem such as this is to use polyfills, which we'll discuss more in depth next. In short, though, a polyfill is a JavaScript alternative that the browser can use when the original implementation is not yet available. This way, you can load the polyfill if the browser needs it, and the rest of the code base can use the functionality through the original interface, and never know which implementation it is working with. In the case of localStorage, we could simply check whether the authentic API is available, and write code that mimics its behavior if it is not available. The following code snippet shows this behavior:

// If the browser doesn't know anything about localStorage,
// we create our own, or at least an interface that respond
// to the calls we'd make to the real storage object.
if (window.localStorage === undefined) {
  var FauxLocalStorage = function() {
    var items = {};
    this.length = 0;

    this.setItem = function(key, value) {
      items[key] = value;
      this.length++;
      };

    this.getItem = function(key) {
      if (items[key] === undefined)
        return undefined;

        return items[key];
      };

    this.removeItem = function(key) {
      if (items[key] === undefined)
        return undefined;

      this.length--;
        return delete items[key];
      };
  };

  // Now there exists a property of window that behaves just like
  // one would expect the local storage object to (although in this example
  // the functionality is reduced in order to make the point)
  window.localStorage = new FauxStorage();
}

// This code will work just fine whether or not the browser supports the real
// HTML5 API for local storage. No exceptions will be thrown.
localStorage.setItem("name", "Rodrigo Silveira");
localStorage.length == 1; // true
localStorage.getItem("name"); // "Rodrigo Silveira"
localStorage.removeItem("name");
localStorage.length; // == 0

Although the preceding polyfill really doesn't store any data beyond the current session, this particular implementation of a local storage polyfill can be enough for the needs of a given application. At the very least, this implementation allows us to code to the official interface (calling the real methods defined by the specification), and no exceptions are thrown by the browser, since the methods are indeed present. Eventually, whenever the browser that didn't support the HTML5 API, and thus used our polyfill because of the conditional that checked for browser support of the feature, that conditional will no longer trigger the polyfill to be loaded, thus the client code will always refer to the original implementation and no changes will be needed to the main source code.

While it is quite exciting to consider what polyfills can do for us, the observant student will quickly notice that writing complete, secure, and accurate polyfills is slightly more complicated than adding simple CSS hacks to a style sheet in order to make a design compatible with different browsers. Even though the sample local storage polyfill shown previously was relatively complicated, it does not completely mimic the official interface, and neither does it behave 100 percent the same with the little functionality that it did implement. Soon the organized student will ask how much time he or she should expect to spend writing bullet-proof polyfills. The answer, which I'm glad to report is a positive one, is given and explained in the next section.

Polyfills

To answer the preceding question, that is, how much time should you expect to spend writing your own robust polyfills in order to be able to start using HTML5 features today, and still have your code run on multiple different browsers is, zero. Unless you really want the experience of writing a fallback for different browsers, there is no reason to cook your own libraries and such, since much work has already been done on this area by hundreds of other developers who have shared their work with the community.

With polyfills, there really isn't a single JavaScript import that we can use at the top of our HTML5 project that will magically extend every deficient browser, and make them 100 percent HTML5 ready. However, there are many separate projects available, so that if you're trying to use a particular element, you can simply import that particular polyfill. While there is no definitive source where all of these polyfills can be found, a simple Google or Bing search for the particular functionality you want should quickly connect you to an appropriate polyfill.

Modernizr

One particular tool that is worth mentioning is Modernizr. This JavaScript library inspects the page that loads it, and detects which HTML5 features are available in the user's browser. This way, we can very easily check whether or not a particular API is available, and take action accordingly.

As of this writing, the current version of Modernizr allows us to test for a particular API or feature, and load specific polyfills in the case that the test returns positive or negative, which makes adding polyfills when needed very easy and effortless.

Furthermore, Modernizr also includes HTML5 Shiv, which is a very small piece of JavaScript that allows us to use all of the HTML5 semantic tags in browsers that don't recognize them. Note that this will not add the actual functionality of the tags, but will merely allow you to style those tags through CSS. The reason is that in Internet Explorer Version 8 and below, if we try to style an element that the browser doesn't recognize, it will simply ignore any CSS applied to it. With Modernizr, however, those elements are created (using JavaScript), so that the browser then knows about the tags, and thus allows CSS to be applied to them.

For more information about Modernizr, check out the official website at http://modernizr.com/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.120.136