Tech Tip: How to browse your website within an eBook

browser

It’s Tom here, one of the developers at Bookry. I want to share a totally cool tip with you that allows you to keep readers within your book while they’re using the Browser widget. If you’ve played around with this widget already you may have noticed that tapping on a link will sometimes take you into Safari. Obviously this isn’t always the best experience, so I want to explain why this happens and how you can keep your readers within your book. This little tip only works if you own the website you’re visiting because you’re going to need to add a small bit of code to your website it to make it work.

So why does the Browser widget open up some links in Safari? Well, it’s all down to iBooks and how it handles navigating within a widget – depending on the type of navigation that the reader uses, it decides whether or not it’s going to open the link in Safari. There are three types of ways a reader can navigate from one page to another:

  1. A reader clicks on a link – iBooks opens the new page in Safari
  2. A reader submits a form – iBooks opens the new page within the widget
  3. Some JavaScript on the page redirects the reader – iBooks opens the new page within the widget.

Number 1 is the most common type of navigation on a webpage, hence the reason why Safari is always being opened. However, with a bit of JavaScript magic, we can utilize number 3 to make number 1 work in the way that we want it to. What we can do is write a small bit of JavaScript that listens for the reader to click on a link, and when this happens, we navigate the reader to where they want to go, but staying within the widget. If done correctly the reader wont know the difference!

If you own your own website and want to embed it within an book you can add some code to the website so that readers always remain within your book. If your website uses templates it’s best to place this in the parent template so that every page on your website is navigable from within the book. Unfortunately if you don’t own the site then readers will still be directed out of the book.

What we’re going to do is only trigger this behavior when we add a URL argument in, but we’re also going to persist this URL argument whilst we navigate around the site. The first step is to search for all anchor elements on a webpage and bind a function to them. In newer browsers, we can also listen for when new anchor elements are added and bind to them too:

(function() {
    var toJsLink = function(link) {
        // TODO
    };

    if (window.location.search.indexOf('bkry-rewrite-links=true') !== -1) {
        // Look for new nodes
        window.addEventListener('DOMNodeInserted', function(e) {
            if (e.target.tagName = 'A') { toJsLink(e.target); }
        }, false);

        // Look for existing nodes
        var links = document.getElementsByTagName('a');
        for (var i = 0; i < links.length; i++) {
            toJsLink(links[i]);
        }
    }
})();

We’ve still got the actual binding to do, but this grabs all the links for us. If the current URL, has the query ‘bkry-rewrite-links’ and it is equal to ‘true’ we search through the webpage and call ‘toJsLink’ with each link we find.

Next we need to look at our toJsLink function. There are a few escape conditions here where we don’t want to redirect the user so, we need to take note of these and not bind to the anchor if any of them are true:

var toJsLink = function(link) {
    // Check the escape conditions

    // We don't have a link to follow to
    if (link.href === undefined) { return; }

    // Some other javascript has bound to onclick
    if (link.onclick) { return; }

    // We've explicitly told ourselves not to do anything with this link
    if (link.className.indexOf('bkry-link-ignore') !== -1) { return; }

    var handler = function(evt) {
        // TODO
    };

    // Bind the event handler down
    if(link.addEventListener) {
        link.addEventListener('click', handler, false)
    } else if(link.attachEvent) { // old ie support
        link.attachEvent('onclick', handler)
    }
};

Once we have checked all the escape conditions we can then go on to binding the actual event handler – that’s our next step. One thing to note here is we should also support older versions of Internet Explorer that use the non-standard ‘attachEvent’ call. Finally we can place some code in the all important handler function. We have an escape conditions here too, for example if the link only changes the URL hash we don’t want to do anything. We also need to update the target URL slightly to carry through the URL argument to tell the next page to re-write the links for the book widget.

var handler = function(evt) {
    // Look out for escape conditions
    if (link.href.indexOf('#')) {
        var targetUrl = link.href.split('#')[0];
        var currentUrl = window.location.href.split('#')[0];
        if (targetUrl === currentUrl) {
            return;
        }
    }

    // Salt in the query string argument to make sure it always gets passed around
    var url = link.href;
    var hash = url.split('#')[1];
    url = url.split('#')[0];
    var qs = url.split('?')[1];
    url = url.split('?')[0];

    evt.preventDefault();

    window.location = url + '?' + (qs || '') + (qs ? '&' : '') + 'bkry-rewrite-links=true' + (hash ? '#' + hash : '')
    return false;
};

Some important things to note here are that we need to retain any existing query string arguments and any URL hash components as the following page may be depending on it. Once we’ve done all this we can happily redirect the reader to their new target.

You can download the complete code here for you to download. If you place it at the very end of your page then it should pickup all your links. It has no dependencies, however if you’re using a library like jQuery you could easily wrap this up in a document.ready() or something else.

Once you have inserted this into your page, update the url in the Browser widget to have the ?bkry-rewrite-links=true at the end and make sure that you do plenty of testing – this is by no means a complete fix to every possible scenario, and use at your own risk. If however you find that you’ve found a bug or improvement then I would love to hear about it and push it out to this tutorial so that more people can apply a fix like this if they want to. Just use any of our feedback links on bookry.com.

Download the complete code example here.