一个小功能引起对事件的回顾

学无止境、温故知新

故事是这样的

昨天,遇到个需求,需要做到类似于图一左边’good’模块的效果,当div滚轮滑动到最底部的时候,最外成文档不滑动。(注意,页面滚动其实是文档不是body)但是,正常情况下,效果应该类似于’bad’模块。

图一

先想想自己怎么实现。

解决方案(一)

当body或者html脱离文档流,页面自然不会滚动。div内部却可以滚动。直接上代码:

1
2
3
4
<div id="good"></div>
$('#good').bind('mousewheel DOMMouseScroll', function(e) {// DOMMouseScroll 兼容火狐
$("body").css({position: 'fixed'}); // 设置fixed脱离文档流
}

或者

1
2
3
4
<div id="good"></div>
$('#good').bind('mousewheel DOMMouseScroll', function(e) {// DOMMouseScroll 兼容火狐
$("body").css({overflow: 'hidden'}); // 设置fixed脱离文档流
}

但是,上面的代码也不是百分百的成功,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
body{
width:100%;
heigth:100%;
}
.child{
width:100%;
heigth:100%;
overflow:scroll;
}
<body>
<div class="child">
内容....
</div>
</body>

而且,通过改变样式的方法来控制滚动,也许会造成页面上其他样式的问题,所以并不完美。

解决方案(二)

方案一并不完美,我们是否可以参数通过事件来阻止滚轮使页面滚动的发生。例如:

1
2
3
$('#good').bind('mousewheel DOMMouseScroll', function(e) {
e.preventDefault(); //阻止滚动
}

这时候,发现body不在滚动了,但是div也不能滚动了。这个方案似乎也陷入了困境。对代码进行改进。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('#good').bind('mousewheel DOMMouseScroll', function(e) {
var scrollTo = null;
if (e.type == 'mousewheel') {
//jq对原生event进行了封装,e.originalEvent等于原生event
scrollTo = (e.originalEvent.wheelDelta * -1);
}
else if (e.type == 'DOMMouseScroll') {
scrollTo = 40 * e.originalEvent.detail;
}
if (scrollTo) {
e.preventDefault();
$(this).scrollTop(scrollTo + $(this).scrollTop());
}
});

这时候需求得到了解决。

滚轮滚动事件基础

  1. mousewheel和DOMMouseScroll只是滚轮滚动的时候触发和scroll是不相同的。
  2. FireFox使用的DOMMouseScroll事件,其他浏览器使用的mousewheel事件,为了兼容一般同时使用。
  3. FireFox中没有wheelDelta属性,使用的是detail,每次向下滚动一次detail的值为120,而wheelDelta的值为-3。

事件冒泡和事件捕获

这是一个老得掉牙的问题,但是还是值得在这里写出的,毕竟是事件的基础知识,不写的话,文章总觉得缺点什么。

(网络拷贝图片)通过上图,可以得知,事件流从window开始进行事件捕获,然后到目标元素,再事件冒泡到到window。

1
2
3
4
5
6
7
8
9
10
11
12
var body = document.getElementsByTagName("body")[0];
var div =document.getElementsByTagName("div")[0];
body.addEventListener("click",function(event){console.log("body捕获"+event.eventPhase)},true)
body.addEventListener("click",function(event){console.log("body冒泡"+event.eventPhase)},false)
div.addEventListener("click",function(event){console.log("div捕获"+event.eventPhase)},true)
div.addEventListener("click",function(event){console.log("div冒泡"+event.eventPhase)},false)
运行结果:
body捕获1
div捕获2
div冒泡2
body冒泡3

addEventListener监听函数最后一个参数为true表示捕获事件,为false表示冒泡,默认为false。ie8及以下不支持捕获。
event.eventPhase表示当前事件流处于什么阶段,1位捕获,2位当前目标,3位冒泡。

事件兼容 IE

事件监听和注销监听

添加:
IE下:attachEvent。attachEvent('on'+type,callback)
其他:addEventListener。 addEventListener(type,callback,[false|true])
移除:
IE下:detachEvent。
其他:removeEvent。

事件event

  1. IE下没有event,使用设置event =window.event。
  2. IE下 event.srcElement等于其他浏览器的event.target。
  3. 阻止默认IE下 event.returnValue = false等于其他浏览器的event.preventDefault();
  4. 一般情况。this在IE下等于window,其他的是this等于event.currentTarget。

    最后,事件还有什么性能优化的呀,事件代理,内存泄漏呀,我也不想说了,头都写晕了。