Javascript中如何检测一个事件在该浏览器中是否存在?

比如musewheel事件,如何检测在当前浏览器的实现中是否可以使用?
如果使用类似document.onmousewheel这样的方式,如何避免这个方法被人恶意(重)定义?

阅读 14.7k
2 个回答

楼上的给出了一段封装好的代码,我尝试讲一下思路什么的。

在大多数的现代浏览器中,你可以通过下面的方式检测在当前浏览器中是否可以用这个事件:

'onclick' in document.documentElement;

嗯,前面说了这段代码只是在大多数浏览器中可用,并不是所有的浏览器中都靠谱。为什么,因为可能会用到一些偏门的事件,比如只有IE支持的mouseentermouseleaveonpropertychange什么的,IE9-中没有oninput事件什么的,或者还有一些其他浏览器中才有的事件等等。这本身就取决于浏览器对标准的实现,和自身的实现。

经评论中@依云的补充,测试发现现代浏览器都兼容了mouseentermouseleave事件。

上面这种方法不靠谱,那就另辟蹊径,尽量进行可靠的测试。比如reset事件通常用于表单重置操作,事实上在某些浏览器下面直接检测document.documentElement - 文档是否支持的时候可能不同的浏览器结果不同。而这个时候可以尝试检测特定的元素是否支持这个事件。毕竟可以粗略的说,在浏览器中事件大部分情况都是用在DOM元素上的。因此,可以还可以这样检测:

'onreset' in document.createElement('input')

通常情况下直接尝试查看浏览器的event属性的类型会发现它会返回字符串'function',便于测试,还可以这样:

var dom = document.createElement('div');
dom.setAttribute('onclick', 'xxx'); // 保险起见,设置浏览器可能已知的事件类型来测试它
typeof dom.onclick; // -> 'function',测试浏览器已知事件类型的时候通常覆写没用,它会正确的返回内置属性
// 也可以换个自定义的事件使用这种方式尝试检测

根据上面这些结论,就可以定义一个通用的函数来检测浏览器是否支持特定的事件了:

function detectEventSupport(eventName) {
    var tempElement = document.createElement('div'),
        isSupported;
    eventName = 'on' + eventName;
    isSupported = (eventName in tempElement); // 使用第一种方式
    // 如果第一种方式行不通,那就来看看它是不是已知事件类型
    if(!isSupported) {
        tempElement.setAttribute(eventName, 'xxx');
        isSupported = typeof tempElement[eventName] === 'function';
    }
    // 清除掉动态创建的元素,以便内存回收
    tempElement = null;
    // 返回检测结果
    return isSupported;
}

至此,一个基本靠谱的测试函数就完备了。

楼上的代码中有个TAGNAMES,然后在后面的检测中用到了。这里实际上只是为了保证测试的完备性和健壮性,列出了一部分已知在这些元素上支持的事件进行测试。

写在最后,正如上面所说。因为可能会用到某些浏览器厂商自定义的事件类型什么的,这种情况下最好查查规范,查查浏览器相关的文档,以确保测试的准确性。而对于多数标准的浏览器事件,上面的方法是能够进行可靠检测的。万一碰到无法确认的,查文档;针对特定的环境进行检测即可,思路有了就好办了。

补充修正

  1. 比如楼上的Modernizr的代码中提供的事件并不一定要每个都进行判断。这些事件浏览器都是支持的。
  2. 在前面的代码中,都是直接对document对象或者div元素进行测试,可能需要处理的元素并不是div,那么针对特定的元素进行处理即可。
  3. 使用key in object的方式进行检测的时候很容易受自定义事件的影响,比如:
document.onxxx = 'xxx';
'onxxx' in document;

这种情况下key in object就显得不那么可靠了。

解释下上面第三种方式,为什么先setAttribute,再用ele[eventName]的方式检测。在标准中规定,使用setAttribute()方法添加已有的属性时会重写指定的属性值。但是事实上,在用这个方法设置已有的事件属性时,然后直接用ele[eventName]访问这个属性,始终返回的是已有的事件属性,而事件属性对应的实现都是function

因而基于这种方式,检测已知事件属性类型时总是返回'function'

先回答前一个问题。

可以参考Modernizr的处理方式:

function is( obj, type ) {
    return typeof obj === type;
}

var isEventSupported = (function() {

  var TAGNAMES = {
    'select': 'input', 'change': 'input',
    'submit': 'form', 'reset': 'form',
    'error': 'img', 'load': 'img', 'abort': 'img'
  };

  function isEventSupported( eventName, element ) {

    element = element || document.createElement(TAGNAMES[eventName] || 'div');
    eventName = 'on' + eventName;

    var isSupported = eventName in element;

    if ( !isSupported ) {
      if ( !element.setAttribute ) {
        element = document.createElement('div');
      }
      if ( element.setAttribute && element.removeAttribute ) {
        element.setAttribute(eventName, '');
        isSupported = is(element[eventName], 'function');

        if ( !is(element[eventName], 'undefined') ) {
          element[eventName] = undefined;
        }
        element.removeAttribute(eventName);
      }
    }

    element = null;
    return isSupported;
  }
  return isEventSupported;
})();
isEventSupported("mousewheel")// Chrome
-> true 
isEventSupported("mousewheel")// Firefox
-> false 
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题