Beginning HTML and CSS is O-U-T

I’ve mentioned it on Google+ and Twitter. I should probably mention it here: Beginning HTML and CSS is out. It came out yesterday.

Here’s proof:

20130311_162525

The book looks great. I’m really happy with it.

Are there things I wish I could redo? Sure. Of course. But overall, based on what I had to work with in terms of existing material, time and other obligations, I don’t think I could improve on what’s on the shelves in any significant way.

I’m very proud of the introduction to jQuery chapter. It was written towards the end of the book and was one of the things I was most interested in writing when I started the project. I think the combination of my own interest in the subject and the fact that I had a solid foundation of cranking out 2-4 pages a day for months at a time made for some really good writing. Easy writing to do, but still pretty good.

If you’re a novice web programmer or an experienced programmer who has never used HTML,CSS and JavaScript to build a web front end, I really think this book will be an invaluable guide to get you up and running.

The Next One

Both books I’ve written (and the other book I was writing that got shelved) have had other authors involved. With Professional jQuery I came in, late in the project, with the goal of finishing it off. I spent the whole time on that book reacting to what was there. I think I did some good work there, but it’s not the book I would write if the project were 100% mine. I would have approached it differently from the first chapter. And even putting structure aside, the chapters that I normally would be most interested in writing (the chapters introducing the jQuery API) were already done when I started in on the book. It was a great experience, just not an optimal one.

This book had existing material as well, so I was once again reacting to the structure and content that was already there. The good thing about this experience is that I was able to use what I wanted and had carte blanche to tear things apart if I felt like it would make a better book.

So, with all that in mind, the next book I write is going to be mine from the ground up. That’s really important to me. While I’ll always listen to opportunities in this space, and I try not to say “never” (although… I’d really to say that I’m never using ExtJS again and know that it will be the gospel truth;) I really don’t want to go through this process again unless the book is wholly mine. Too much is invested in this kind of work to not be able to steer the content to the greatest possible degree. Obviously, writing books is collaborative and I’m not just being polite when I thank all the people involved (notably Carol Long, Katherine Burt and Dan Maharry here) there’s still a level of control I’ve yet to achieve having never truly been the sole author of one of these things.

I’d like to do that.

I’d also like it to be shorter. I’d love to write a thinner volume since it’s difficult to maintain book writing pace over a long period of time. 2-300 pages sounds pretty good to me right now.

Not that I know what it would be.

(Rob runs off to think about the next thing)

Using the Geolocation API

The following is probably the last long-form article you’ll see here for a while. I’m in full on book mode for the next couple of months, so I’m not expecting to be writing a ton here for the foreseeable future.

Anyway, the following is actually inspired by the kind of work we’re going to be doing for the book. I’m not actually doing any code-heavy writing for the book, so I wanted to get my hands dirty with this sort of content. It’s fun.

As an aside, I’ll have another (short) post about the book shortly. I’ve got a full allotment of co-authors and I’d like to give them, and the project, a little shine before I turn into a writing hermit.

And now… Geolocation


One of the most powerful aspects of mobile web app development is the ability to blur the line between the real world and the world on the screen. Allowing users to interact with physical places in novel ways is driving startups across the world and is infiltrating some of the most popular sites and applications on the web. Facebook, Foursquare, Twitter, Google+ and countless other services have built the idea of location into the core of their applications. You too can do the same in your mobile web app by taking advantage of the well-supported Geolocation API.

Whether it’s interaction with your own location based services or with a third party API, like the Google Maps API in use in this recipe, the journey begins with getting the user’s latitude and longitude.

In this article you’ll learn how to:

  • Use the W3C Geolocation API to get a user’s latitude and longitude
  • Smoothly handle devices without Geolocation support, providing a reasonable fallback for older devices
  • Use the Google Maps API to place a marker indicating the user’s location, labeled with a friendly place name

The Basics of the Geolocation API

Before we dive into the heart of the example let’s quickly look at the Geolocation API.

The elevator pitch is to the point- the W3C’s Geolocation API allows developers to retrieve the geographical location of a device. It became a Candidate Recommendation, the level at which the W3C deems features and functionality pretty much settled, in September of 2010 and already has support across a variety of devices and browsers. At the present time it’s supported all the major smartphone browsers and even on the desktop it’s supported by all major browsers except Internet Explorer 6, 7, 8 and Safari 3.2 and 4.0.

As a note, the Geolocation API was heavily influenced by the analogous functionality provided by the Google Gears plugin. This is why you often see the mothballed Google Gears plugin referenced as a fallback in many geolocation examples.

The API itself is straightforward. It provides a navigator.geolocation object which in turn provides two methods (watchPosition and getCurrentPosition) which allow the browser to query the device’s location through the use of location information servers. If you’re getting a location for use in a search or in a check-in, then getCurrentPosition is the method you want to use as it’s designed for a single location lookup. If you’re tracking a user’s location over time, then watchPosition is the way to go since it’s designed to be used over a longer period of time.

Location information is pulled from a variety of sources including IP address, device GPS, Wi-Fi and Bluetooth MAC address, RFID, or Wi-Fi connection location. The different level of precision inherent in these several methods is exposed by the API as an accuracy property.

Now that we’ve taken a look at the Geolocation API, let’s walk through our code in depth.

Getting Started with the Geolocation API

While the Geolocation API is straightforward, getting it up and running smoothly in the real world is a little bit tricky. Accounting for a successful result is one thing, making sure there’s a decent response for browsers without geolocation capability or in other instances where geolocation isn’t available is another.

Our example will touch on ways to minimize these issues and will illustrate the basics of a successful request.

Testing for the Geolocation object and Querying the User’s Location

The first thing you’ll need to do when working with Geolocation is to test whether or not it’s actually available in the browser. This is done by testing against the presence of the navigator.geolocation object. As you’ll see in the following code sample, this is a simple if…else block with a call to the navigator.getCurrentPosition() method when the object is present and a fallback when it’s not available.

The method getCurrentPosition() takes three arguments:

  1. the function to run on a successful location request
  2. the function to run as when the request fails
  3. a PositionOptions object containing other optional configuration objects.

In our case we’re passing in two named functions, success and failure, and an optional timeout of five seconds, which will keep things moving if something goes awry with the request.

Listing 1 Testing for the presence of the navigator.geolocation object

if (navigator.geolocation){  // does the geolocation object exist?             
  navigator.geolocation.getCurrentPosition( 
    success, 
    failure, 
    {timeout:5000} 
  );          
} else {
  failure();  
}      

In addition to the timeout seen in the previous example the PositionOptions object accepts two other options- enableHighAccuracy and maximumAge. enableHighAccuracy indicates that you would like receive the best possible results at the potential cost of speed or battery performance. maximumAge sets a limit, in milliseconds, for the age of a cached position object. The browser caches recent position location responses. Setting maximumAge to 0 will immediately force the browsers to try to obtain a new location.

Handling a successful geolocation request

The first function we’ll look at it is our success function. You can see it in Listin 1.2

In our example we’re using the Google Maps API to display a marker with the user’s current location.

The function accepts a single data argument. This argument is automatically passed into the function by the geolocation API. This is object is defined in the specification to contain two properties coords and timestamp. timestamp is, as expected, a timestamp indicating the age of the position information. For this example we’re most interested in the coords object which contains a latitude/longitude pair indicating the user’s position.

Moving on from the single argument, you’ll see several Google Maps specific variables.

The first, GM,represents a simple technique to speed up JavaScript. By creating a local representation of the google.maps object we save lookups to the global space. In general, local variables are faster. This is especially important with mobile which devices don’t have the fastest JavaScript engines.

Every little bit helps.

The most important piece, from a geolocation perspective, is the use of two properties, data.coords.latitude and data.coords.longitude, to build a new Google Maps LatLng object. The LatLng object is a core component of Google Maps. At its core it’s a latitude/longitude pair enhanced with methods and properties used throughout the API. To create one in our example you simply pass it the two properties of the data.coords object. We store that in our position variable.

We now have our user’s location, ready to place on the map.

The next section we’re using the Google Maps Geocoder to get a friendly label for the user’s location. Geocoding works in two ways. Normal geocoding means you pass the service an address string and it will return a series of geographical results. In our case we’re doing reverse geocoding, which means we pass the service a latitude/longitude pair and the service returns whatever it knows about the location.

Listing 2 Successfully handling a geolocation request

var success = function( data ){ //the data object, passed into success
  var GM = google.maps,
      mapOptions = {
        zoom: 12,
        center:  defaultPosition,
        mapTypeId:  GM.MapTypeId.ROADMAP
      },
      map = new GM.Map(  document.getElementById('map'), mapOptions),
      position = new GM.LatLng( 
        data.coords.latitude, //accessing the coords property
        data.coords.longitude 
      ),      
      niceAddress = "Your location",
      geocoder = new GM.Geocoder();
      geocoder.geocode( { 'latLng' : position  }, 
        function( results, status ) {
          if ( status == GM.GeocoderStatus.OK ) {
            if (results[0]) {
              niceAddress = results[0].formatted_address;
            }
          } 
          var infowindow = new GM.InfoWindow({
            map: map,
            position: position,
            content: niceAddress
          });
        });
      map.setCenter(position);
    }
  
  

Handling a Geolocation Failure

Our failure function handles two negative situations. If the user doesn’t have a geolocation enabled browser or if there’s an error in the geolocation lookup, this function is ready to step in and save the day. The failure function can be seen in Listing 3

You’ll see the setup is similar to the success function with a Google Maps object being created with some smart defaults.

The major difference is in the way we get the latitude and longitude for the map. Instead of getting the coordinates from a geolocation response we create a simple form to allow the user to enter their location. Inside the formResponse function we use then use the Google Maps Geocoding service to get a latitude and longitude pair corresponding to the location in the form submission.

Additionally we use the geolocation error response, if it exists, to build out a slightly more useful error message. If a browser supports geolocation and has some issue with the location request it should return an error response as the single argument to the provided callback function.

Listing 3 The failure Function

var failure = function( error ){ //The potential error response
  var  GM = google.maps,
       mapOptions = {
         zoom: 12,
         center:  defaultPosition,
         mapTypeId:  GM.MapTypeId.ROADMAP
       },
       map = new GM.Map(  document.getElementById('map'), mapOptions),
       formResponse = function(e){
         var geocoder = new GM.Geocoder(),
             position = defaultPosition,
             niceAddress =  "Sorry We Couldn't Find Your Location";
         geocoder.geocode(
           { 'address':  document.getElementById("location").value }, 
           function( results, status ) {
             if ( status ==  GM.GeocoderStatus.OK ) {
               if (results[0]) {
                 niceAddress =  results[0].formatted_address;
                 position = new GM.LatLng( 
                   results[0].geometry.location.lat(),
                   results[0].geometry.location.lng() 
                 )
               }
             } 
           var options = {
             map : map,
             position :  position,
             content :  niceAddress
            },
           infowindow = new  google.maps.InfoWindow(options);
           map.setCenter(options.position);
           document.getElementById("geocode").style.display="none";
         }
       )
     return false;
   }
   var  fallback = document.createElement("form");
   fallback.id="geocode";
   if ( error ) { 
     switch(error.code) {//Error Handling based on error.code   
      //HANDLE ERRORS//
   }      
 }  
 fallback.innerHTML  = "<label for='location'>Eneter Your Location" + 
  "<input  type='text' id='location' /></label><input type='submit'  />";
 fallback.onsubmit  = formResponse;
 document.getElementById("main").appendChild(  fallback );
};

The error response contains a code object indicating the type of error and a human readable message string that’s defined to be used in debugging or for error logs. There are four potential values for the error.code. These can be seen in Table 1:

Table 1 Possible error response codes

Code

Name

Definition

0

UNKNOWN_ERROR

The location lookup failed due to an undefined error

1

PERMISSION_DENIED

The location lookup failed because the application does not have permission to use the Geolocation API.

2

POSITION_UNAVAILABLE

The position of the device could not be determined.

3

TIMEOUT

The location lookup took longer than the length of time defined in

Putting it all together

Listing 4 shows the completed function. In it we’ve wrapped our two methods into a larger function called loadMap. Doing so allows us to streamline the code by creating a single set of defaults for the map and to encapsulate all of the functionality under a single function so as to keep the global namespace as neat as possible.

Listing 4 The Completed Function

var loadMap = function(){
  var GM = google.maps, 
      defaultPosition = new GM.LatLng(42, -71),
      mapOptions = {
      zoom: 12, 
      center: defaultPosition, 
      mapTypeId: GM.MapTypeId.ROADMAP},
    map = new GM.Map( 
      document.getElementById('map'), 
      mapOptions
    ), 
    success = function( data ){
      var position = new GM.LatLng( 
        data.coords.latitude, 
        data.coords.longitude 
      ), 
         niceAddress = 'Your location',
         geocoder = new GM.Geocoder();
      geocoder.geocode({ 
        'latLng': position },
         function( results, status ) {
           if ( status == GM.GeocoderStatus.OK ){
             if (results[0]) {
               niceAddress = results[0].formatted_address; 
             }
           }
           var infowindow = new GM.InfoWindow({
             map: map, 
             position: position, 
             content: niceAddress
           });
         }
       );
      map.setCenter( position );
    }, 
    failure = function( error ){ 
      var formResponse = function(){ 
        var geocoder = new GM.Geocoder(),
            position = defaultPosition, 
            niceAddress = 'Sorry We Couldn't Find Your Location';
        geocoder.geocode({
     'address':document.getElementById('location').value 
    }, 
          function( results, status ) {
            if ( status == GM.GeocoderStatus.OK ){ 
              if (results[0]) {
                niceAddress = results[0].formatted_address; 
                position = new GM.LatLng(
                  results[0].geometry.location.lat(),
                  results[0].geometry.location.lng() 
                )
               } 
             }
             var options = {
                map: map, 
                position: position, 
                content: niceAddress
              },
              infowindow = new google.maps.InfoWindow(options);
              map.setCenter(options.position);
              document.getElementById('geocode').style.display='none';
           }
         )
       return false; 
     } 
     var fallback = document.createElement('form');
     fallback.id='geocode';
      if ( error ) {
       switch(error.code) {
         case error.PERMISSION_DENIED:
     	    fallback.innerHTML += "<p>You chose not share geolocation data. Please, use the form below. </p>" ;  
         break;  
	 case error.POSITION_UNAVAILABLE:
            fallback.innerHTML += "<p>Sorry, we couldn't determine your location. Please, use the form below. </p>" ;
         break;  
         case error.TIMEOUT: 
            fallback.innerHTML += "<p>Sorry, the location request time out. Please, use the form below. </p>" ;
         break;  
         default: 
            fallback.innerHTML += "<p>Sorry, there was an error. Please use the form below. </p>" ;
         break;
       }  	
    }
    fallback.innerHTML += "<label for='location'>Eneter Your Location <input type='text' id='location' /></label><input type='submit' />";
    fallback.onsubmit = formResponse;
    document.getElementById("main").appendChild( fallback );
  };
  if (navigator.geolocation){
    navigator.geolocation.getCurrentPosition( success, failure, {timeout:5000} ) ;			
  } else {
    failure();	
  } 	
}

Summary

With that we’ve walked through the basics of the geolocation API. While our example utilizes the Google Maps API, any exploration of location based services can be based on the same pattern. Use the navigator.geolocation object where available, and then design in a simple fallback for non-supporting browsers and devices.

code used in the geolocation sample

In Cased You Missed It… Isobar Code Standards Updated and Posted to Github

So, as you probably noticed I did a bunch of writing for IBM earlier this year. Because of that I’ve got about a million draft posts here that I’ve got sitting in various states of completion. I’m going to work my way through them over the next few weeks. You’ve been warned (in a good way, I hope.)

Anyway, this happened a couple of weeks ago, but it’s still interesting (and important to me personally) so I thought I’d point it out again, in case you missed it the first time.

Over at Isobar we recently relaunched our Front-end Code Standards & Best Practices document and posted it up on Github to solicit feedback and changes from the community.

This document has made a strong impression in the year plus since Paul announced it and because of that we take it pretty seriously. To that end we wanted to update the design a little bit (just because it needed it) and have revisited a lot of the content to make sure it remains fresh and still reflects the current trends of web development.

So, check it out and let us know if there’s anything we can improve.

My Latest IBM Article is Live: “An introduction to Ajax”

An introduction to Ajax.

Get a technical introduction to Ajax programming, and discover the core JavaScript code and popular library implementations. This article presents a brief history of the technology, then outlines the technical basics of Ajax interactions using core JavaScript coding as well as three popular JavaScript libraries.

My New IBM Article is Live: “HTML5, CSS3, and related technologies”

In it, I make a small argument for using HTML5 as a generic marketing term. I’ll be following up on that a little later, here on this blog.

Anyway, check it out. The resources section is worth it alone.

HTML5, CSS3, and related technologies.

Web standard development and marketing

It’s a great time to be a web developer. After a long period of hibernation, the standards bodies and browser vendors have been extremely busy over the past few years, generating a torrent of exciting technology. Developers are greedily seizing on this work, producing demos and full-blown applications at a steady pace. Fed by this activity and further boosted by the growth of their standards-capable mobile browsers, companies like Google and Apple are using these new standards to market their products and services. The wider press is also seizing on this wave and pushing standards hype well beyond the normal circle of web developers and browser vendors.

This volume of discussion has obvious benefits, of course. People getting excited about web standards is a positive development for everyone in the industry. From that perspective, the persistent use of blanket terms, especially HTML5, as a sort of brand shorthand for “emerging web technology” is a useful shortcut. It allows nontechnical people to grasp—in a generalized way—the exciting work being done in the standards space right now.

Interestingly, even the W3C has gotten into the act, using HTML5 and its associated logo (see Figure 1) to publicize the “web platform.”

Read the rest…