Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

1) Ditch caching in `isEventSupported`, to make example (and mechanis…

…m of this inference) easier to understand.

2) Change `isEventSupported` to use generic element (DIV), when supplied object doesn't implement `Node` interface (and so has no `setAttribute`).
Something like `isEventSupported('hashchange', window)` now internally tries `<DIV>.setAttribute('hashchange', '')`, since `window` is one of `setAttribute`-less objects.
This change fixes some of the false positives from previous verion.

3) Add few more HTML5, as well as proprietary "pageshow" and "pagehide" events to test page.
  • Loading branch information...
commit 6320eda339b9e38a4d634b1e4ff738c828a4ccb8 1 parent 224714a
Juriy Zaytsev authored December 18, 2009
61  isEventSupported.js
@@ -23,6 +23,45 @@ var isEventSupported = (function(){
23 23
     'select':'input','change':'input',
24 24
     'submit':'form','reset':'form',
25 25
     'error':'img','load':'img','abort':'img'
  26
+  }
  27
+  
  28
+  function isEventSupported(eventName, element) {
  29
+
  30
+    element = element || document.createElement(TAGNAMES[eventName] || 'div');
  31
+    eventName = 'on' + eventName;
  32
+    
  33
+    // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
  34
+    var isSupported = (eventName in element);
  35
+    
  36
+    if (!isSupported) {
  37
+      // if it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
  38
+      if (!element.setAttribute) {
  39
+        element = document.createElement('div');
  40
+      }
  41
+      if (element.setAttribute && element.removeAttribute) {
  42
+        element.setAttribute(eventName, '');
  43
+        isSupported = typeof element[eventName] == 'function';
  44
+
  45
+        // if property was created, "remove it" (by setting value to `undefined`)
  46
+        if (typeof element[eventName] != 'undefined') {
  47
+          element[eventName] = void 0;
  48
+        }
  49
+        element.removeAttribute(eventName);
  50
+      }
  51
+    }
  52
+    
  53
+    element = null;
  54
+    return isSupported;
  55
+  }
  56
+  return isEventSupported;
  57
+})();
  58
+
  59
+var isEventSupportedWithCache = (function(){
  60
+  
  61
+  var TAGNAMES = {
  62
+    'select':'input','change':'input',
  63
+    'submit':'form','reset':'form',
  64
+    'error':'img','load':'img','abort':'img'
26 65
   }, 
27 66
   cache = { };
28 67
   
@@ -40,15 +79,21 @@ var isEventSupported = (function(){
40 79
     // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
41 80
     var isSupported = (eventName in element);
42 81
     
43  
-    if (!isSupported && element.setAttribute && element.removeAttribute) {
44  
-      element.setAttribute(eventName, 'return;');
45  
-      isSupported = typeof element[eventName] == 'function';
46  
-      
47  
-      // if property was created, "remove it" (by setting value to `undefined`)
48  
-      if (typeof element[eventName] != 'undefined') {
49  
-        element[eventName] = void 0;
  82
+    if (!isSupported) {
  83
+      // if it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
  84
+      if (!element.setAttribute) {
  85
+        element = document.createElement('div');
  86
+      }
  87
+      if (element.setAttribute && element.removeAttribute) {
  88
+        element.setAttribute(eventName, '');
  89
+        isSupported = typeof element[eventName] == 'function';
  90
+
  91
+        // if property was created, "remove it" (by setting value to `undefined`)
  92
+        if (typeof element[eventName] != 'undefined') {
  93
+          element[eventName] = void 0;
  94
+        }
  95
+        element.removeAttribute(eventName);
50 96
       }
51  
-      element.removeAttribute(eventName);
52 97
     }
53 98
     
54 99
     element = null;
21  test.js
@@ -27,13 +27,13 @@ w('keyup');
27 27
 document.write('<h2>HTML events</h2>');
28 28
 
29 29
 w('load');
30  
-w('unload', typeof window.onunload != 'undefined' ? window : void 0 );
  30
+w('unload', window);
31 31
 w('abort');
32 32
 w('error');
33 33
 
34 34
 document.write('<h2>View events</h2>');
35 35
 
36  
-w('resize');
  36
+w('resize', window);
37 37
 w('scroll');
38 38
 
39 39
 document.write('<h2>Form events:</h2>');
@@ -89,7 +89,7 @@ w('stop', document);
89 89
 w('readystatechange');
90 90
 w('beforeprint', document.body);
91 91
 w('afterprint', document.body);
92  
-w('beforeunload', typeof window.onbeforeunload != 'undefined' ? window : void 0 );
  92
+w('beforeunload', window);
93 93
 
94 94
 document.write('<h2>Unexistent (most likely) events:</h2>');
95 95
 
@@ -118,4 +118,17 @@ document.write('<h2>HTML5 events</h2>');
118 118
 w('hashchange', document.body);
119 119
 w('online', document.body);
120 120
 w('offline', document.body);
121  
-w('message', typeof window.onmessage != 'undefined' ? window : void 0 );
  121
+w('message', window);
  122
+w('undo', document.body);
  123
+w('redo', document.body);
  124
+w('storage', window);
  125
+w('popstate', window);
  126
+
  127
+w('canplay', document.createElement('video'));
  128
+w('seeking', document.createElement('video'));
  129
+w('seekend', document.createElement('video'));
  130
+
  131
+document.write('<h2>Proprietary</h2>');
  132
+
  133
+w('pageshow', window);
  134
+w('pagehide', window);

3 notes on commit 6320eda

Olmo Maldonado

What about wrapping this file in an anonymous function and sharing the TAGNAMES between the two methods?

Olmo Maldonado

Is style the reason why the method was not inlined?

Olmo Maldonado

Not in this commit, but should the declared function inside of isEventSupportedWithCache should be named isEventSupportedWithCache. It's currently isEventSupported.

Olmo Maldonado

I've consolidated both methods into one. Since the second only returns the cache iff the cache is populated. Which means that the isEventSupported is called. Moreover, the setting of the cache is only done if the lookup failed.

Was there a reason you wanted to split the cache vs non-cache?

http://gist.github.com/259644

Juriy Zaytsev
Owner

@ibolmo
I actually wanted to delete cached version altogether, but copied and renamed it to another method for now. I want to keep this particular method as an example of this technique, without spicing it up with memoization or any other optimizations.

isEventSupportedWithCache is not used anywhere on a test page, but might still be useful for demonstration purposes.

Feel free to fork it and tweak as desired ;)

Please sign in to comment.
Something went wrong with that request. Please try again.