Making this blog work as an app on the iPhone (the wrap up)

So far in this series (one, two, three) we have specialized content, detection of phone orientation, basics of being Web Clip capable, and a back to home page functionality. This post wraps it all up by removing the latter, implementing some simple Back button functionality, a “loading” indicator, and I do a little bit of code housekeeping too.

Before we implement a back button, we need to implement a proper URL stack so that we can save where we’ve been in order that the Back button works properly. Since JavaScript’s arrays have push() and pop(), we might as well make use of an array (I’ve called it visitStack). It’s going to be a little peculiar though: we shall be pushing the page’s URL as we load it. That means when we use the Back button, the top of the stack will contain the current page, not the previous one. We shall have to pop off the current page’s URL, and then pop off the previous page’s URL in order to load it. The home page is “special” in that we don’t push it onto the stack: instead we shall use the fact that the stack is empty to signal that we’re moving back to the home page. The home page has some specific behavior – namely it doesn’t have a back button – that we want to trap.

Right. Now the Back button. I decided to use an image, rather than trying to implement it directly.

Back Button

I added it to the header div and gave it a name:

<img id="backbutton" alt="Back button" title="Back button" src="/somepath/backbutton.png" />

The CSS positions the button absolutely inside the area covered by the header div. I didn’t want to futz with floats, etc, trying to get it right, so I just placed it. It’s also described as display:none to begin with so that the code will display it as and when necessary.

#backbutton {display:none; position:absolute; top:27px; left:2px;}

OK, that’s it with the display semantics, time for some code. Here’s the code that shows and hides the button; it also adds/removes a click handler for the button:

  var showBackButton = function () {
    $("#backbutton").show().on("click", function (e) {
      var thisUrl = visitStack.pop();
      if (visitStack.length === 0) {
        loadPage("http://blog.boyet.com/", true);
      }
      else {
        var backUrl = visitStack.pop();
        loadPage(backUrl, false);
      }
    });
  };

  var hideBackButton = function () {
    $("#backbutton").off("click").hide();
  };

The hideBackButton() function is the simplest: it just removes the click handler and then hides the button. The showBackButton() function is a little more complex and we see visitStack in use. The code shows the button and then adds a click event handler to it. The event handler pops off the current page’s URL and then checks to see if the URL stack is empty. If it is, the home page is loaded. If it isn’t, the previous URL is popped off, and that URL is loaded. (Recall that the second parameter to loadPage() defines whether the URL defines the home page (true) or not (false).)

The rest of the stuff happens in a much changed loadPage() function. First of all I had to create a separate boolean (isOnHomePage) to define whether the current page is the home page or not (it’s set to true initially). Using this made it simple to detect the case where the user was navigating away from the home page to a secondary page and the Back button had to be displayed.

  var loadPage = function (url, isForHomePage) {
    window.scrollTo(0, 0);
    addProgressDiv();
    $("#wrapper").load(url + " #wrapper", function () {
      fixupLinks();

      if (isForHomePage) {
        hideBackButton();
        isOnHomePage = true;
      }
      else {
        if (isOnHomePage) {
          isOnHomePage = false;
          showBackButton();
        }
        visitStack.push(url);
      }
      removeProgressDiv();
    });
  };

So, taking it step by step: first we scroll the page to the top as before, we then display a progress div (I’ll get to that in a moment), and then we load the wrapper div. Compared with last time, I’ve moved everything inside the completion function for the load() function. Once the content has been loaded, the completion function fires. First thing to do is fix up all the anchor links like before. After that we get to the new stuff: if the URL just loaded was for the home page, we hide the Back button and set our flag to denote that we’re now showing the home page. If it wasn’t for the home page, but we’re in fact moving away from the home page, we show the Back button (and turn off the “on home page” flag). We then push the new page’s URL onto the stack. Finally, after all other processing, we remove the progress div.

A quick aside on the progress div: it’s just a div that gets positioned absolutely and that has some simple “Loading…” text. Here’s the CSS for it:

#progress {display:none; 
  position:absolute; top: 100px; left:60px; width:200px; margin: 0 auto; padding:20px 0;
  text-align:center; 
  font-size:20px;
  background-color: rgba(0,0,0,0.6); color:white; -webkit-border-radius:10px; }

And here’s the two functions that display it and hide it. Note that I’m adding the div dynamically, it’s not part of the original markup. I could equally well have done this with the Back button of course.

  var addProgressDiv = function () {
    $(document.body).append("
Loading...
"
); $("#progress").show(); }; var removeProgressDiv = function () { $("#progress").hide().remove(); };

And here is what it looks like (along with the Back button):

Progress div

Here is the current code as a whole, so you can better understand how it all fits together (there are a couple of tweaks elsewhere as well).

$(function () {
  var 
    canOrient = false,
    isOnHomePage = true,
    visitStack = [];

  var changeOrientation = function () {
    if (canOrient) {
      var currentOrientation = window.orientation;
      if ((currentOrientation === 0) || (currentOrientation === 180)) {
        $(document.body).css("width", 320);
        $("#progress").css("width", 200);
      }
      else {
        $(document.body).css("width", 480);
        $("#progress").css("width", 360);
      }
    }
  };

  var wireUpOrientationChange = function () {
    canOrient = window.orientation !== undefined;
    if (canOrient) {
      window.onorientationchange = changeOrientation;
    }
  };

  var addProgressDiv = function () {
    $(document.body).append("
Loading...
"
); $("#progress").show(); }; var removeProgressDiv = function () { $("#progress").hide().remove(); }; var showBackButton = function () { $("#backbutton").show().on("click", function (e) { var thisUrl = visitStack.pop(); if (visitStack.length === 0) { loadPage("http://blog.boyet.com/", true); } else { var backUrl = visitStack.pop(); loadPage(backUrl, false); } }); }; var hideBackButton = function () { $("#backbutton").off("click").hide(); }; var updateContent = function () { changeOrientation(); fixupLinks(); }; var loadPage = function (url, isForHomePage) { window.scrollTo(0, 0); addProgressDiv(); $("#wrapper").load(url + " #wrapper", function () { updateContent(); if (isForHomePage) { hideBackButton(); isOnHomePage = true; } else { if (isOnHomePage) { isOnHomePage = false; showBackButton(); } visitStack.push(url); } removeProgressDiv(); }); }; var fixupLinks = function () { $("#wrapper a").on("click", function (ev) { var url = ev.target.href, re = /blog\.boyet\.com/i; if (url && url.match(re)) { ev.preventDefault(); loadPage(url, false); } }); }; wireUpOrientationChange(); updateContent(); });

Again, if you want to see how this all looks on your iPhone, you need to open up blog.boyet.com in Safari, touch the middle button in the button bar, select “Add to Home Screen”, give it a name (I use “Boyet blog” since it just fits), and hit Return. At that point, it’s ready to be used on your Home screen.

(Quick notes: the CanOrient flag was to help when I was using desktop Safari to debug the code – desktop Safari has an option whereby you can get it to pretend to be an iPhone, but of course there is no orientation feature. The progress div also participates in the orientation code: I widen it for the case the phone is in landscape mode. I also decided to grow/shrink the body element instead of futzing with the individual named divs.)

Now playing:
Digital Analog Band - The Blues
(from The Best)


Loading similar posts...   Loading links to posts on similar topics...

No Responses

Feel free to add a comment...

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response