Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sammy.js routes not firing from Knockout-bound links in IE10

I have a single page application that uses Knockout.js for data binding and Sammy.js for routing client-side (hash-based) URLs.

I'm seeing a weird problem in Internet Explorer, however: sometimes links, when clicked, will change the URL in the browser's address bar, but the corresponding Sammy route will not execute.

It doesn't happen every time (but I can consistently reproduce the error), and it only happens in IE10 (Chrome works fine every time). It appears to be related to Knockout as well, since a set of hard-coded links don't exhibit the same problem.

To illustrate, I've stripped away everything but the bare minimum to recreate the problem and created two jsbin examples:

Example 1 (with Knockout): http://jsbin.com/aretis/2/

To see the problem, open the link above and click "Record #1", then "Baz", then "Record #1" again. The URL for record 1 will appear in the address bar, but the route for that record will not be appended to the list.

Example 2 (without Knockout): http://jsbin.com/amivoq/1/

In this example, I have a static list of record links instead of a data-bound list. Clicking on any of the links (in any order) will result in that route being appended to the list (as it should).

A reminder that these must be run in IE to reproduce the problem.

Any ideas?

like image 960
Matt Peterson Avatar asked Jul 31 '13 22:07

Matt Peterson


1 Answers

As per my comment above, I worked around this problem by just catching the window.hashchange event and parsing out the URL myself. This is the only part of Sammy.js I was really using and I wasn't having any luck tracking down the actual problem. Hopefully, this will help someone else.

The first thing I did was bind the hashchange event:

$(function () {
    $(window).on("hashchange", HandleUrl);

    // Call our URL handler to deal with any initial URL given to us.
    HandleUrl();
}

This calls the following URL parser:

function HandleUrl() {
    var hash = location.hash;

    if (hash.indexOf("#Account") >= 0) {
        var splitParts = hash.split("/");

        if (splitParts.length >= 2) {
            ShowLoadingBox();
            ShowAccountDetailFromId(splitParts[1]);
        }
    } else if (hash.indexOf("#Contact") >= 0) {
        var splitParts = hash.split("/");

        if (splitParts.length >= 2) {
            ShowLoadingBox();
            ShowContactDetailFromId(splitParts[1]);
        }
    } else if (hash.indexOf("#ThingsToDo") >= 0) {
        SwitchToPanel("navPanelThingsToDo");
    } else if (hash.indexOf("#ThingsIveDone") >= 0) {
        SwitchToPanel("navPanelThingsIveDone");
    } else if (hash.indexOf("#Reports") >= 0) {
        SwitchToPanel("navPanelReports");
    } else {
        SwitchToPanel("navPanelMyAccounts");
    }
}

The functions like ShowAccountDetailFromId() and SwitchToPanel() just show and populate (using Ajax calls to web services) the appropriate <div>. This is probably a completely naive approach, but it is working (i.e. you can bookmark URLs, the back button and browser history work, etc.) My apologies for the non-answer "answer".

like image 185
Matt Peterson Avatar answered Sep 21 '22 11:09

Matt Peterson