28

I'm interested in using the JavaScript hashchange event to monitor changes in the URL's fragment identifier. I'm aware of Really Simple History and the jQuery plugins for this. However, I've reached the conclusion that in my particular project it's not really worth the added overhead of another JS file.

What I would like to do instead is take the "progressive enhancement" route. That is, I want to test whether the hashchange event is supported by the visitor's browser, and write my code to use it if it's available, as an enhancement rather than a core feature. IE 8, Firefox 3.6, and Chrome 4.1.249 support it, and that accounts for about 20% of my site's traffic.

So, uh ... is there some way to test whether a browser supports a particular event?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Will
  • 283
  • 1
  • 3
  • 4
  • FYI, the phrase you're looking for is not "progressive enhancement" but rather "graceful degradation". Progressive enhancement is when you make your website accessible to all users but add features to a more specific group of users (JavaScript-enabled, HTML5-supporting, etc). Graceful degradation is when you depend on certain functionality, but have a backup method if it is not supported by the client. Not that it really matters. :) – Sasha Chedygov May 20 '10 at 22:07
  • 4
    Hmm. I would argue that "graceful degradation" means that a web site continues to function even when functionality it ordinarily relies on is unavailable. By contrast, "progressive enhancement" is when a site enables extra features that it does not rely on, only when the user agent supports them. I'm planning on writing this particular code in such a way that it does not modify the location.hash property at all unless the hashchange event is available. So that's a progressive enhancement. And yeah, not that it really matters -- they're just two sides of one coin. – Will May 21 '10 at 15:13

4 Answers4

34

Well, the best approach is not going through browser sniffing, Juriy Zaytsev (@kangax) made a really useful method for detecting event support:

var isEventSupported = (function(){
  var TAGNAMES = {
    'select':'input','change':'input',
    'submit':'form','reset':'form',
    'error':'img','load':'img','abort':'img'
  }
  function isEventSupported(eventName) {
    var el = document.createElement(TAGNAMES[eventName] || 'div');
    eventName = 'on' + eventName;
    var isSupported = (eventName in el);
    if (!isSupported) {
      el.setAttribute(eventName, 'return;');
      isSupported = typeof el[eventName] == 'function';
    }
    el = null;
    return isSupported;
  }
  return isEventSupported;
})();

Usage:

if (isEventSupported('hashchange')) {
  //...
}

This technique is now used in some libraries like jQuery.

Read more here:

mercator
  • 28,290
  • 8
  • 63
  • 72
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • That's a very interesting method... I particularly like the extra check of seeing if `'return;`' gets converted to a function automatically. – gnarf May 20 '10 at 20:22
  • This looks like the most robust method. Thanks for posting it! – Will May 20 '10 at 20:38
  • 4
    +1 for mentioning jQuery, [jQuery is really the best, it solves all kinds of browser problems and is good, as well](http://www.doxdesk.com/img/updates/20091116-so-large.gif) – BlueRaja - Danny Pflughoeft May 20 '10 at 20:39
  • 1
    @BlueRaja, I'm only mentioning jQuery as a reference, that a serious library is relying on this technique. – Christian C. Salvadó May 20 '10 at 23:36
  • Credit where it's due: Thank you @CMS for this trick! I used it to answer http://stackoverflow.com/questions/13086400/feature-detection-for-ability-to-drop-file-over-html-file-input/ – joequincy Oct 29 '12 at 15:19
  • 1
    This method does not work for the 'onhashchange' event. The function has a flaw in that it will miss any events that are attached to the window object only. Also, it does not make sense to add "'hashchange':'window'" to the list of TAGNAMES since a new window element cannot be created, I believe. With "isEventSupported('hashchange')" the createElement() function will return a div which won't have the onhashchange event as a property and so the test fails even though window.onhashchange might be true. – Dermot Doherty Oct 04 '13 at 15:42
  • 1
    @CMS - You should add prefix checking as well – vsync Jun 18 '15 at 11:50
  • it returns false for `readystatechange` on Chrome even though it works – zok Mar 01 '19 at 16:56
  • "Detecting event support without browser sniffing" Link now broken (404) – phi lo czar Apr 01 '22 at 07:02
26

The Mozilla documentation suggested the following:

if ("onhashchange" in window) {
    alert("The browser supports the hashchange event!");
}

This works in IE8 and the Chrome 5 beta too (I didn't test Chrome 4.1), and correctly fails in Opera and Safari.

Edit: the Mozilla page I linked to no longer contains any suggestions. I can't find any other Mozilla docs containing suggestions for event detection either.

mercator
  • 28,290
  • 8
  • 63
  • 72
  • Very elegant. I may use a version of this modified with some ideas from the other answer that got posted. Thanks. – Will May 20 '10 at 20:38
  • @Keavon it works for me. But you have to use `"onclick"`. But also see the accepted answer and the blog it links to. – mercator Mar 10 '23 at 13:28
  • @mercator Ah, I forgot the `on` prefix, thanks. I'll delete my comment. – Keavon Mar 11 '23 at 00:04
2

Here is a modification of the answer provided by CMS, which doesn't contain a function in another, which I think would work:

function event_exists(eventName){
    if(typeof(eventName)!='string' || eventName.length==0)return false;
    var TAGNAMES = {
        'select':'input','change':'input',
        'submit':'form','reset':'form',
        'error':'img','load':'img','abort':'img'
    }
    var el = document.createElement(TAGNAMES[eventName] || 'div');
    eventName = 'on' + eventName;
    var isSupported = (eventName in el);
    if (!isSupported) {
        el.setAttribute(eventName,'return;');
        isSupported = (typeof(el[eventName])=='function');
    }
    el = null;
    return isSupported;
}
Community
  • 1
  • 1
Antkhan
  • 47
  • 1
  • Why do you need to create a new element? What's wrong with just doing a simple `typeof` check on the event class? (see [my answer](https://stackoverflow.com/a/69890629)) – typeracer Nov 08 '21 at 22:28
0

I think the simplest solution is to just do a typeof check on the event class.

For example: typeof(InputEvent) returns "function" in Chrome and "undefined" in Internet Explorer.

typeracer
  • 759
  • 8
  • 11