这篇文章发布于 2015年12月18日,星期五,00:07,归类于 JS实例。 阅读 107012 次, 今日 6 次 47 条评论
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=5092
一、开场暖身
网上常见蹲来蹲去的小段子,比方说:“李代沫蹲,李代沫蹲,李代沫蹲完黄海波蹲;黄海波蹲,黄海波蹲,黄海波蹲完宁财神蹲;宁财神蹲,宁财神蹲,宁财神蹲完张耀扬蹲;张耀扬蹲,张耀扬蹲,张耀扬蹲完郭美美蹲;郭美美蹲,郭美美蹲,郭美美蹲完……”。应该源自“萝卜蹲,萝卜蹲,萝卜蹲完苹果蹲……”。
在网页中,滚动条的滚动行为也是类似的调调,如果页面出现多个内嵌滚动条,则行为表现是:子元素滚,子元素滚,子元素滚完父元素滚;父元素滚,父元素滚,父元素滚完容器滚……
比方说下面:
在妹子脸上滚,先是妹子滚,妹子滚完主页面滚,对吧~
//zxx: 别问为什么不使用张含韵,因为张妹子照片是横的,滚动空间小,晓得伐~
这是浏览器的默认行为,如果我们遇到了一个需求:子元素滚,子元素滚完,就完了,父元素不需要滚了。那该如何实现呢?
在PC端,OK,本文介绍的方法,值适用于PC端,移动端,咳咳,我15年就没做过移动端项目,不好意思,手生,我也没去研究。
补充于翌日
移动端的处理,可以参见@hacke2的这篇文章:“在移动端上使用原生滑屏解决方案”
二、阻止浏览器默认行为的特定套路
哈,本文标题有些拗口,实际上用一句话概括就是:如何阻止浏览器的默认滚动行为。
基本上,好像印象中就没有例外的,阻止浏览器的默认行为,就一条(假设事件对象参数是event):event.preventDefault()
.
这是标准规范使用方法。但是,对于老IE浏览器,event.returnValue = false
. 如果你使用jQuery等框架,直接上面的event.preventDefault()
就可以,库已经帮你搞定了兼容细节处理。
OK,回到本文。阻止默认滚动,也是类似,关键是找到准确的事件。
第一反应是scroll
事件,不知道是不是我测试的方法不对,结果没鸟用;其实想想也可以理解,scroll事件要触发,尼玛必须已经滚动了哈~
后来,发现要从滚动事件的源头处理起来。在PC端,绝大多数滚动都是鼠标滚动触发的(上下快捷键也可以滚动页面,但一般人不知道),因此,我们可以从鼠标滚轮事件入手。
三、鼠标滚轮事件
JS基础知识的啦,mousewheel
事件:
dom.onmousewheel = function() {
// 嘿嘿嘿
};
IE, Chrome都认识,但是FireFox浏览器,要使用DOMMouseScroll, 具体知识呢我之前有写过文章分析过:“JS滚轮事件(mousewheel/DOMMouseScroll)了解”。现在回过头看看这篇文章,内容和点都挺好。但是,当时正好在学习模块化开发,以及JavaScript语言模式,所以,提供的代码,科科,不是拿来主义的调调,所以这篇文章没有火啊~
扯远了,总之呢,我们对鼠标滚动这个事件,进行event.preventDefault()
,页面就像齿轮卡壳了一样,滚不动了!
四、原理爬上来
找到了关键钥匙,现在就要开门了。
子元素可以滚,父元素不能滚。
我们可以对子元素写上鼠标滚轮事件,对吧,的那个子元素滚动到边界的时候,我们立马插一刀event.preventDefault()
。干掉整个页面的滚动,世界一下子安静了,时间好像突然静止了一般,好像很不错的样子哦!
于是,寡人我屁颠屁颠搞起代码(粗糙示意):
if (direction == 'up' && scrollTop == 0) { event.preventDefault() }
翻译下就是:哥哥我往上滚,当滚到头的时候,页面滚动歇菜。
Chrome一测试,喔噢,好棒,鼓掌! FireFox一测试,喔噢,好棒too,鼓掌again! IE一测试,喔噢,好…………尼玛,滚蛋了~ 滚动高度直接跳过了0,直接把父元素给滚了。
靠,什么鬼?不兼容,搞不定,怎么办?
五、临界手动翻滚
就是说,我们不要到0或者最大滚动高度时候,再去阻止默认滚动,我们要在到达边界的前一个滚动,就开始下手,手动滚动到边界,同时event.preventDefault()
阻止鼠标滚动行为。于是,IE浏览器也棒棒哒了!
说实话,从开头到现在,中文啪啪啪敲了这么多,其实毛线用都没有,从度娘或谷哥过来的同学需要的不是什么神神叨叨的废话,需要的只是下面这段可以直接拿来主义的代码,好吧,拿去吧——子元素滚完就滚完的方法源代码:
$.fn.scrollUnique = function() { return $(this).each(function() { var eventType = 'mousewheel'; // 火狐是DOMMouseScroll事件 if (document.mozHidden !== undefined) { eventType = 'DOMMouseScroll'; } $(this).on(eventType, function(event) { // 一些数据 var scrollTop = this.scrollTop, scrollHeight = this.scrollHeight, height = this.clientHeight; var delta = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta : -(event.originalEvent.detail || 0); if ((delta > 0 && scrollTop <= delta) || (delta < 0 && scrollHeight - height - scrollTop <= -1 * delta)) { // IE浏览器下滚动会跨越边界直接影响父级滚动,因此,临界时候手动边界滚动定位 this.scrollTop = delta > 0? 0: scrollHeight; // 向上滚 || 向下滚 event.preventDefault(); } }); }); };
没错,依赖jQuery的一个扩展方法,上面代码只要拷贝到你页面的JS中,然后,你希望哪个元素滚动到底,父级不滚动,直接:
$().scrollUnique();
就可以了,然后就可以打卡下班了。
对了,有个demo, 您可以狠狠地点击这里:里面元素滚动到底外部容器不滚动demo
如果您的显示器竖屏,或者宽度1920的,会发现右侧没有大滚动条,则,麻烦大家手动高度改小,拉拉窗口啊,或者打开控制台之类的。
//zxx: 你问我什么不加高页面造一个滚动条?唉,舍不得把底部的广告刻意藏在滚动条之外~
六、抛砖引玉
前文也提到,页面滚动条滚动的事件源很多,不仅仅是鼠标滚动,上下键,End键, Home键等都有滚动定位行为。因此,大家要想100%全方位封杀滚动行为,仅仅上面的鼠标滚动代码是不够的,但是,关键钥匙已经给大家了,大家可以依次,按照自己的项目需求进行进一步深入拓展。
不过,我个人觉得,上面mousewheel
处理已经足够了,什么键盘触发滚动,让他自己去玩耍吧,还是别折腾了,吃力不讨好。
哟,写完了,抬头一看,一张截图都没有,这可不行,风水不能断,搞一张。
恩,不错,真正的无可挑剔的「截」图。
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:http://www.zhangxinxu.com/wordpress/?p=5092
(本篇完)
- JS滚轮事件(mousewheel/DOMMouseScroll)了解 (0.649)
- DOM基础小测27期答疑文字版-窗体滚动二三事 (0.390)
- CSSOM视图模式(CSSOM View Module)相关整理 (0.317)
- 来了来了,scrollend滚动停止事件也支持了 (0.250)
- 小tips: 滚动容器尺寸变化子元素视觉上位置不变JS实现 (0.206)
- 如何实现页面刷新后不定位到之前的滚动位置? (0.206)
- CSS scroll-behavior和JS scrollIntoView让页面滚动平滑 (0.178)
- 小tips: 元素focus页面不滚动不定位的JS处理 (0.178)
- 尝试使用JS IntersectionObserver让标题和导航联动 (0.178)
- 几个常见功能重合DOM API的细节差异 (0.144)
- div模拟textarea文本域轻松实现高度自适应 (RANDOM - 0.033)
tips: 提醒一下大家,onwheel -> wheel
知道了一个新事件 …..
(event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta : -(event.originalEvent.detail || 0);
这句代码目前在 Mac Chrome 是有问题的(windows 不知道),mousewheel 事件的 event 对象没有 originalEvent对 象,应该直接访问 event.wheelDelta。
代码年代久远,已经跑不动了~
iframe有问题
chrome中有bug,外层滚动时,内层未加top值,也随之滚动
原生的怎么写 大佬
大佬,牛逼~~~~
第一次看技术文觉得像在看段子
哈哈哈
开心地学到了知识
谢谢博主!
横向滚动咋办啊,还有mac上不用鼠标的情况咋整呢?
方法有时候在火狐上不好使,是因为判断是否为火狐浏览器的方法有点问题,换其他方法判断火狐浏览器赋值type即可,比如 navigator.userAgent.indexOf(“Firefox”) > -1
最后方法,亲测火狐,chrome,ie9可用
$.fn.scrollUnique = function() {
return $(this).each(function() {
var eventType = ‘mousewheel’;
// navigator.userAgent.indexOf(“Firefox”) > -1
// document.mozHidden !== undefined
if (navigator.userAgent.indexOf(“Firefox”) > -1) {
eventType = ‘DOMMouseScroll’;
}
// console.log(eventType)
$(this).on(eventType, function(event) {
// 一些数据
var scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = this.clientHeight;
var delta = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta : -(event.originalEvent.detail || 0);
if ((delta > 0 && scrollTop <= delta) || (delta < 0 && scrollHeight – height – scrollTop 0? 0: scrollHeight;
// 向上滚 || 向下滚
// event.preventDefault();
var e = window.event || event;
if (e.stopPropagation){
e.stopPropagation();
}
else {
e.cancelBubble = true;
}
if (e.preventDefault){
e.preventDefault();
}
else {
e.returnValue = false;
}
}
});
});
};
好几个中文符号,尤其是那个减号(中文),不好看不出来
demo在火狐不好使啊=。=
event.preventDefault();
该方法将通知 Web 浏览器不要执行与事件关联的默认动作。所以这不是很适合大部分页面设计,存在一定的bug,
可以用下面代码替代
var _e = window.event || e;
if (e.stopPropagation){
e.stopPropagation();
}
else {
e.cancelBubble = true;
}
if (e.preventDefault){
e.preventDefault();
}
else {
e.returnValue = false;
}
可以试试这个兼容360.
(function($) {
$.fn.scrollUnique = function() {
return $(this).each(function() {
var eventType = ‘mousewheel’;
if(document.mozHidden !== undefined) {
eventType = ‘DOMMouseScroll’;
}
$(this).on(eventType, function(e) {
var e0 = e.originalEvent;
var delta = e0.wheelDelta || -e0.detail;
this.scrollTop += (delta < 0 ? 1 : -1)*120;
e0.preventDefault();
});
});
};
})(jQuery);
IF那个判断语句可以直接去掉.直接上 this.scrollTop += (delta < 0 ? 1 : -1)*120;亲测 360下快速滚动没毛病!
我想问一下:如果这个设计到后台表格数据的时候。
当前表格是iframe出现滚动条,当滚动的时候出发父级滚动条如何处理
直接在子元素scroll事件里阻止冒泡不可以吗?
真的敲喜欢张真人的文章,看工具文还能这么一行一个笑点,真难为你了啊
实测!在360浏览器, 版本8.1.1.240 内核版本 45.0.2454.101极速模式下
上面的代码有问题, 当滚动鼠标滚轮速度比较慢时可能没啥大问题,但是当滚轮滚动的速度非常快的时候,就会发现页面还是会一起滚动,我在想是不是要做一个延迟处理!
看我的最新评论.
张师兄,看你的文章是一种享受~
知道原因了,wheelDelta的滚动距离是120,而图片高度又不超过,直达底部,就像没有缓冲一样直接到低了,如何解决继续看看-0-
前辈,发现在chrome里滚动不是很流畅,您当时使用的版本会吗?在moz的浏览器里相当流畅。
目前是chrome56
讲得很好,但是有些文章配图要有底线啊,上班时候看这个文章简直了
旭哥,还是那么幽默….
多谢大神,学习啦
学习了很多。。行文也很幽默,捐赠了
貌似在移动端无效?
大神的CSS文章有二百多篇了,有时候遇到问题回来看下,可是每次翻页找实在是太累了,可否整理个标题目录呢?
@自命菜菜 右上角有搜索~
新版的chrome已经不可用了。
return false是可以的。
求兼容横向滚动
张大哥,小弟不才,这今天在学javascipt,有个简单的问题请帮忙释疑解惑,不甚感谢。
function getNextElement(node){
if(node.nodeType == 1){
return node
}
if(node.nextSibling){
return getNextElement(node.nextSibling)
}
return null
}
这段函数中return getNextElement(node.nextSibling)这句怎么理解?函数里面包含函数自身的引用?
简单递归, 找到当前node的下一个element node
Firefox 桌面版貌似本身就已经是这样了?
才知道和大神来自一个小城镇,对大神更加钦佩了,学习ing,希望早日摆脱渣渣头衔
收藏学习了。哈哈
滚动定位的时候,你的代码是:this.scrollTop = delta > 0? 0: scrollHeight;
但是,是否 this.scrollTop = delta > 0? 0: scrollHeight – scrollTop; 会更合理呢?谢谢!
@deng 你测试过?你确信?scrollHeight是没问题的,更合理~
我有遇到这种疑惑过。左边是菜单(有滚动条),右边是套一个iframe,然后iframe滚动完,左边的菜单也跟着滚动。。。。不过我暂时还没找到办法解决,反正不是很重要,先做其他事情先了~~
现在遇到个问题就是,想把整个网站整成只有一个css,但感觉还是挺有难度,页面太多,css也很多。。现在是慢慢弄一个system.css是通用的,比如弹窗、按钮、输入框、form表单横向布局、竖向布局、字体颜色等等~~然后有的页面感觉就是独立css布局的感觉,依赖于另外一个css文件,感觉是只能重写页面了。。(注:我是中途进这家公司的,刚好遇上系统重构,所以想把css这块也搞好。再注:我是看了你那篇2010写的那篇文章进来的)
星哥 有一个问题。
元素没有滚动条就不能滚动么?
overflow hidden 了 怎么滚动元素,只能监听 wheel事件 改变top? 哦 还要加个内容框。但是这样好麻烦。
有没有简单一点的方法?
接上条,我看到“JS滚轮事件(mousewheel/DOMMouseScroll)了解”这篇文章了里有对event.delta相关的处理,wheelDelta除以了120,detail处理了3,最后event.delta为1是向上滚动,为-1是向下滚动。
鼠标向下滚动时,wheelDelta为-120,而火狐detail为3;鼠标向上滚动时, wheelDelta为120,而火狐detail为-3。如果遇到非火狐的浏览器,请问这里的wheelDelta为什么不需要除以40?
因为只需要判断是向上滚还是向下,所以只需要知道他们的正负就可以了,并不需要知道确切的值。
太棒啦,刚好在纠结这个问题,非常感谢~
http://www.hacke2.cn/scroll-in-uc/ 移动端解决该问题。。
学到了,谢谢