17

I have some code (written by another developer) that is doing AJAX page loading inside of WordPress (e.g. no page reloads) when you click a nav item, AJAX refreshes the primary content area. My problem is that it's broken in IE7 and I have no idea where to start in terms of debugging.

The original opening lines were

var queue = 0;

$('document').ready(function() {
    window.addEventListener("hashchange", hashChange, false);

    // Define window location variables
    var windowHost = window.location.host,
        windowHash = window.location.hash,
        windowPath = window.location.pathname;

But I changed them to make the addEventListener conditional on the basis of whether that method was present or not. Some research told me that the method is not available in older versions of IE (e.g. 7 in my case). Also, the IE7 debug console was identifying that as an unavailable method, so that's pretty clear. I rewrote the lines as follows, but the code is still not working:

var queue = 0;

$('document').ready(function() {
    if(window.addEventListener) {
        window.addEventListener("hashchange", hashChange, false);
    }
    else if (window.attachEvent) {
        window.attachEvent("hashchange", hashchange, false);    
    }
    // Define window location variables
    var windowHost = window.location.host,
        windowHash = window.location.hash,
        windowPath = window.location.pathname;

The full original script can be viewed in this pastebin: http://pastebin.com/Jc9ySvrb

Brian
  • 3,920
  • 12
  • 55
  • 100

2 Answers2

31
  • attachEvent requires events to be prefixed with on.
  • You've different capitalizations for the method. Change hashchange in attachEvent tohashChange to get the event to work in IE8.
  • Use the suggested implementation to support the hashchange implementation for IE7- and other old browsers.

I have created a cross-browser implementation, which adds the hashchange feature to browsers, even those who do not support it. The fallback is based on the specification.

//function hashchange  is assumed to exist. This function will fire on hashchange
if (!('onhashchange' in window)) {
    var oldHref = location.href;
    setInterval(function() {
        var newHref = location.href;
        if (oldHref !== newHref) {
            var _oldHref = oldHref;
            oldHref = newHref;
            hashChange.call(window, {
                'type': 'hashchange',
                'newURL': newHref,
                'oldURL': _oldHref
            });
        }
    }, 100);
} else if (window.addEventListener) {
    window.addEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
    window.attachEvent("onhashchange", hashChange);    
}

Note: This code is useful for one hashchange event. If you want to add multiple hashchange handlers, use the following method.
It defines two functions, addHashChange and removeHashChange. Both methods take a function as an argument.

(function() {
    if ('onhashchange' in window) {
        if (window.addEventListener) {
            window.addHashChange = function(func, before) {
                window.addEventListener('hashchange', func, before);
            };
            window.removeHashChange = function(func) {
                window.removeEventListener('hashchange', func);
            };
            return;
        } else if (window.attachEvent) {
            window.addHashChange = function(func) {
                window.attachEvent('onhashchange', func);
            };
            window.removeHashChange = function(func) {
                window.detachEvent('onhashchange', func);
            };
            return;
        }
    }
    var hashChangeFuncs = [];
    var oldHref = location.href;
    window.addHashChange = function(func, before) {
        if (typeof func === 'function')
            hashChangeFuncs[before?'unshift':'push'](func);
    };
    window.removeHashChange = function(func) {
        for (var i=hashChangeFuncs.length-1; i>=0; i--)
            if (hashChangeFuncs[i] === func)
                hashChangeFuncs.splice(i, 1);
    };
    setInterval(function() {
        var newHref = location.href;
        if (oldHref !== newHref) {
            var _oldHref = oldHref;
            oldHref = newHref;
            for (var i=0; i<hashChangeFuncs.length; i++) {
                hashChangeFuncs[i].call(window, {
                    'type': 'hashchange',
                    'newURL': newHref,
                    'oldURL': _oldHref
                });
            }
        }
    }, 100);
})();
// Usage, infinitely many times:
addHashChange(function(e){alert(e.newURL||location.href);});
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 2
    The event listener is bound correctly now, but will not do anything in IE7, because it is [not supported](http://caniuse.com/hashchange). [IE8 do support](http://msdn.microsoft.com/en-us/library/cc891506(v=vs.85).aspx) the `hashchange` event though. For IE7, you have to detect hashchanges through `setInterval`. – Rob W Feb 18 '12 at 09:13
  • Well you certainly earned your reputation here @RobW - thanks a million. You just saved me hours, I honestly don't know if it's worth going through the whole `setInterval` thing. Looks like a mega headache. – Brian Feb 18 '12 at 09:16
  • @Brian Quite easy to implement. Store the value of `location.hash` in a variable. Run `setInterval` with a small enough interval, say 100. In the `setInterval` method, compare the previous value of `location.hash` with the current value. If they're different, fire a custom hashchange event, and store the new value of `location.hash` in the variable. – Rob W Feb 18 '12 at 09:21
  • @RobW - hey if I'm not a jerk for asking, can you show me what that looks like? I think it would benefit other people looking at this question with the same IE7 incompatibility issue... – Brian Feb 18 '12 at 09:41
  • @Brian Added a solid cross-browser implementation. Throughly tested. – Rob W Feb 18 '12 at 10:34
  • @RobW, solid answer. Still not sure, though, why the top-voted answer isn't just "don't do this yourself, use jQuery" :D – Coderer Nov 05 '12 at 17:49
  • @RobW - this is solid. Thanks for sharing. – Etai Jan 05 '14 at 17:41
  • 1
    @RobW Good work, but shouldn't you use `oldHref = newHref` **after** calling `hashChange`? – Oriol Feb 22 '14 at 17:47
  • @RobG Why do you use `_oldHref`? Wouldn't it work calling `hashChange` with `oldHref`, and after that update `oldHref`? – Oriol Feb 22 '14 at 20:04
  • 1
    @Oriol Imagine that the `hashChange` function throws an error. Then `oldHref` would never be reset, and the function be called over and over again. To avoid this issue, I store it in a local variable before using it. – Rob W Feb 22 '14 at 20:53
  • @RobW Could you explain what is going on here please, specifically the need for hashChangeFuncs? – Ian Lunn May 21 '14 at 17:18
  • 1
    @IanLunn These [add|remove]HashChange functions offer an abstraction to bind the hashchange events. `hashChangeFuncs` is a list of registered callbacks, to be invoked when the hash changes. – Rob W May 21 '14 at 17:23
  • Useful! Seems like in IE8 there is no newURL or oldURL though – Joel Sep 01 '14 at 15:23
  • @Joel You could remove the `else if (window.attachEvent) { ...}` part if you need newURL/oldURL in IE8. – Rob W Sep 01 '14 at 15:26
  • @RobW I did exactly that. Just worth noting. – Joel Sep 02 '14 at 11:19
3

attachEvent takes on two params:

bSuccess = object.attachEvent(sEvent, fpNotify)

[And is needed for all versions of IE below IE9! :( See MDN reference ]

This could work:

if(window.addEventListener) {
    window.addEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
    window.attachEvent("onhashchange", hashchange);//SEE HERE...
    //missed the on. Fixed thanks to @Robs answer.
}

Of course if it is possible, you should just use JQuery, since it encapsulates all this for your.

And as always there is a plugin out there: http://benalman.com/projects/jquery-hashchange-plugin/

gideon
  • 19,329
  • 11
  • 72
  • 113
  • 2
    That's great, thanks for the tipoff. Question: Even in IE7 JS debug mode, I'm not getting any errors from the function itself (only the `addEventListener` before I made it conditional). Any other suggestions for debug? I tried your code but still no dice... frustratingly, no errors either :/ – Brian Feb 18 '12 at 09:06