滚轮事件是一个非常有用的事件,在翻页或放大时,通常都用到它。但它在各浏览器中实现兼容是相对比较困难的,号称最标准的FF,用了一个私有实现DOMMouseScroll,而使用mousewheel的其他浏览器或多或少有点bug。我们先看各浏览器对它的支持程度吧。
IE | firefox | safari | chrome | opera | |
---|---|---|---|---|---|
window对象 | false | true | true | true | true |
文档对象 | true | true | true | true | true |
元素节点 | true | true | true | true | true |
接着我们看一下,如何在火狐中实现滚轮事件的事件分派:
window.addEventListener("DOMMouseScroll",function(event){ alert(event.type) alert(event.clientY) },false); var event = document.createEvent("MouseEvent"); //为了证明分派成功,特意将其clientY设为90 event.initMouseEvent("DOMMouseScroll",true, null, window,0,0,0,0,90,false,false,false,false,0,null); window.dispatchEvent(event)
<script type="text/javascript">
window.onload = function(){
window.addEventListener("DOMMouseScroll",function(event){
alert(event.type)
alert(event.clientY)
},false);
var event = document.createEvent("MouseEvent");
//为了证明分派成功,特意将其clientY设为90
event.initMouseEvent("DOMMouseScroll",true, null, window,0,0,0,0,90,false,false,false,false,0,null);
window.dispatchEvent(event)
}
</script>
<style type="text/css">
</style>
</head>
<body>
</body>
</html>
我们可以看到虽然其他标准浏览器也支持这个名为DOMMouseScroll的事件发派,但当我们手动滚动鼠标滑轮时,也只有FF有反应,弹出两个alert。对于其他标准浏览器,我们改用mousewheel试试。
window.addEventListener("mousewheel",function(event){ alert(event.type) alert(event.clientX) },false); var event = document.createEvent("MouseEvent"); //为了证明分派成功,特意将其clientX设为120 event.initMouseEvent("mousewheel",true, null, window,0,0,0,120,0,false,false,false,false,0,null); window.dispatchEvent(event)
<script type="text/javascript">
window.addEventListener("mousewheel",function(event){
alert(event.type)
alert(event.clientX)
},false);
var event = document.createEvent("MouseEvent");
//为了证明分派成功,特意将其clientX设为120
event.initMouseEvent("mousewheel",true, null, window,0,0,0,120,0,false,false,false,false,0,null);
window.dispatchEvent(event)
</script>
<style type="text/css">
</style>
</head>
<body>
</body>
</html>
这个在FF的以外的标准浏览器见效,FF只能实现事件分派,不能实现手动触发。可以说,如果非原浏览器原生支持的事件类型,addEventListener是一概不理会的。
对于IE就简单得多了,它没有初始化事件的步骤,你也无法设置其属性,因为那是只读的,但可以设置自定义属性。
window.onload = function(){ document.attachEvent("onmousewheel",function(){ var e = window.event; alert(e) alert(e.type) alert(e.aa) }); var event = document.createEventObject(); event.aa = "司徒正美" document.fireEvent("onmousewheel",event) }
<script type="text/javascript">
window.onload = function(){
document.attachEvent("onmousewheel",function(){
var e = window.event;
alert(e)
alert(e.type)
alert(e.aa)
});
var event = document.createEventObject();
event.aa = "司徒正美"
document.fireEvent("onmousewheel",event)
}
</script>
<style type="text/css">
</style>
</head>
<body>
</body>
</html>
说完事件分派,就到事件绑定部分。虽然现在只有FF在用DOMMouseScroll,但考虑到未来FF改用的mousewheel的可能,我们还是写个程序来检测其需要的事件类型吧。最后我们还要在回调函数中修正其滚动属性:
- IE的属性名为detail,w3c那边为wheelDelta,现在统一为delta。
- IE等往上滚一圈为120,往下滚一圈为-120。w3c那边往上滚一圈为-3,往下滚一圈为3,但opera9x系列却实现错误,与IE滚动方向一致,不过10后又修复。
- safari早期版本,wheelDelta会出现浮点数的情况,我们需要自行取整。
function addEvent(el, type, callback, useCapture ){ if(el.dispatchEvent){//w3c方式优先 el.addEventListener( type, callback, !!useCapture ); }else { el.attachEvent( "on"+type, callback ); } return callback;//返回callback方便卸载时用 } var wheel = function(obj,callback){ var wheelType = "mousewheel" try{ document.createEvent("MouseScrollEvents") wheelType = "DOMMouseScroll" }catch(e){} addEvent(obj, wheelType,function(event){ if ("wheelDelta" in event){//统一为±120,其中正数表示为向上滚动,负数表示向下滚动 var delta = event.wheelDelta //opera 9x系列的滚动方向与IE保持一致,10后修正 if( window.opera && opera.version() < 10 ) delta = -delta; //由于事件对象的原有属性是只读,我们只能通过添加一个私有属性delta来解决兼容问题 event.delta = Math.round(delta) /120; //修正safari的浮点 bug }else if( "detail" in event ){ event.wheelDelta = -event.detail * 40//为FF添加更大众化的wheelDelta event.delta = event.wheelDelta /120 //添加私有的delta } callback.call(obj,event);//修正IE的this指向 }); }
用法:
wheel(document,function(e){ alert(e.delta) });
<!DOCTYPE HTML>
<html>
<head>
<title>滚轮事件mousewheel by 司徒正美</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
body{
padding:10px 100px;
}
.slider{
width:48px;
height:200px;
padding: 5px 0px;
background:#eee;
cursor:n-resize;
}
.slider-slot {
width:16px;
margin:10px 15px;
height:180px;
background:#eee;
border:1px solid gray;
border-color:#999 white white #999;
position:relative;
}
.slider-trigger {
width:14px;
height:18px;
font:1px/0 arial;
border:1px solid gray;
border-color:white #999 #999 white;
background:#ccc;
position:absolute;
}
</style>
<script type="text/javascript">// <![CDATA[
window.onload = function(){
function log(s){
window.console && console.log(s);
}
var get = function(i) {
return document.getElementById( i );
}
function addEvent(el, type, callback, useCapture ){
if(el.dispatchEvent){//w3c方式优先
el.addEventListener( type, callback, !!useCapture );
}else {
el.attachEvent( "on"+type, callback );
}
return callback;//返回callback方便卸载时用
}
var wheel = function(obj,callback){
var wheelType = "mousewheel"
try{
document.createEvent("MouseScrollEvents")
wheelType = "DOMMouseScroll"
}catch(e){}
addEvent(obj, wheelType,function(event){
if ("wheelDelta" in event){//统一为±120,其中正数表示为向上滚动,负数表示向下滚动
var delta = event.wheelDelta
//opera 9x系列的滚动方向与IE保持一致,10后修正
if( window.opera && opera.version() < 10 )
delta = -delta;
//由于事件对象的原有属性是只读,我们只能通过添加一个私有属性delta来解决兼容问题
event.delta = Math.round(delta) /120; //修正safari的浮点 bug
}else if( "detail" in event ){
event.wheelDelta = -event.detail * 40//为FF添加更大众化的wheelDelta
event.delta = event.wheelDelta /120 //添加私有的delta
}
callback.call(obj,event);//修正IE的this指向
});
}
function preventDefault(e){
if( e.preventDefault )
e.preventDefault();
e.returnValue = false;
}
wheel(get("number"), function(e){
this.value = (Number(this.value) || 0) + e.delta //120 ;
this.select();
preventDefault(e)
})
wheel(get("img"), function(e){
this.style.width = this.offsetWidth + e.delta + 'px';
this.style.height = this.offsetHeight + e.delta + 'px';
preventDefault(e)
})
function range( num, max,min ) {
return Math.min( max, Math.max( num,min ) );
}
var tar = get('sliderTrigger');
wheel(get("slider"), function(e){
preventDefault(e)
tar.style.top = range( tar.offsetTop + ( -1 * e.delta * 10 ),160,0 ) + 'px';
})
}
</script>
</head>
<body>
<h2>文本框增加/减少值</h2>
<div><input type="text" value="300" id="number">
<span>文本框获得焦点后滚动鼠标滚轮</span>
</div>
<h2>鼠标滚动缩放图片</h2>
<div>
<img id="img" style="cursor:-moz-zoom-in" title="鼠标滚动 缩放大小" src="http://www.baidu.com/img/baidu.gif" />
</div>
<h2>鼠标滚动控制滑块移动</h2>
<div id="slider" class="slider">
<div class="slider-slot">
<div id="sliderTrigger" class="slider-trigger">
<strong> </strong>
<strong> </strong>
<strong> </strong>
<strong></strong>
<strong></strong>
</div>
</div>
</div>
</body>
</html>
Event property | Applies to event: | Up 1 click | Up 2 clicks | Down 1 click | Down 2 clicks |
e.wheelDelta
Supported in Non |
onmousewheel and in non FFbrowsers |
120 | 240 | -120 | -240 |
e.detail
Supported in FF and |
DOMMouseScroll and in FF(as of FF3.x) |
-1 | -2 | 1 | 2 |
在FF官网还给出一个兼容方案:
// creates a global "addwheelListener" method // example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } ); (function(window,document) { var prefix = "", _addEventListener, onwheel, support; // detect event model if ( window.addEventListener ) { _addEventListener = "addEventListener"; } else { _addEventListener = "attachEvent"; prefix = "on"; } // detect available wheel event if ( document.onmousewheel !== undefined ) { // Webkit and IE support at least "mousewheel" support = "mousewheel" } try { // Modern browsers support "wheel" WheelEvent("wheel"); support = "wheel"; } catch (e) {} if ( !support ) { // let's assume that remaining browsers are older Firefox support = "DOMMouseScroll"; } window.addWheelListener = function( elem, callback, useCapture ) { _addWheelListener( elem, support, callback, useCapture ); // handle MozMousePixelScroll in older Firefox if( support == "DOMMouseScroll" ) { _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture ); } }; function _addWheelListener( elem, eventName, callback, useCapture ) { elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) { !originalEvent && ( originalEvent = window.event ); // create a normalized event object var event = { // keep a ref to the original event object originalEvent: originalEvent, target: originalEvent.target || originalEvent.srcElement, type: "wheel", deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1, deltaX: 0, delatZ: 0, preventDefault: function() { originalEvent.preventDefault ? originalEvent.preventDefault() : originalEvent.returnValue = false; } }; // calculate deltaY (and deltaX) according to the event if ( support == "mousewheel" ) { event.deltaY = - 1/40 * originalEvent.wheelDelta; // Webkit also support wheelDeltaX originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX ); } else { event.deltaY = originalEvent.detail; } // it's time to fire the callback return callback( event ); }, useCapture || false ); } })(window,document);