32

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?

2
  • 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. :) Commented May 20, 2010 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. Commented May 21, 2010 at 15:13

4 Answers 4

37

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:

Sign up to request clarification or add additional context in comments.

9 Comments

That's a very interesting method... I particularly like the extra check of seeing if 'return;' gets converted to a function automatically.
@BlueRaja, I'm only mentioning jQuery as a reference, that a serious library is relying on this technique.
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.
@CMS - You should add prefix checking as well
|
29

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.

3 Comments

Very elegant. I may use a version of this modified with some ideas from the other answer that got posted. Thanks.
@Keavon it works for me. But you have to use "onclick". But also see the accepted answer and the blog it links to.
@mercator Ah, I forgot the on prefix, thanks. I'll delete my comment.
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;
}

1 Comment

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)
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.

1 Comment

You are checking whether the event argument constructor exists. That approach does only work if the event does indeed have a specific argument type. So for example this would not work to check whether scroll event is supported as it only has the generic Event argument type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.