手机网站表层div滑动,导致底层body滑动(touchmove的阻止)

案例描述

body很长,可以滑动,body头部有一个模拟下拉的选择框。下拉选择有滚动轴,如下图。

我给body一个overflow:hidden和高度是没有用的。手机网站上背景还是可以滑动,然后我给body一个touchmove的preventdefault()阻止事件,body滑动阻止了,PC上面是可以了,但是手机上面滑动div还是会导致底部body的滑动,我给div 一个阻止冒泡的事件stopPropagation(),手机网站上面还是不可以。

关于preventdefault和stopPropagation,后面有时间会讲解其区别。

解决方案

我经过反复测试,发现滚动轴滚到底部的时候,会触发body的滑动,那么我就在事件滚到底部的时候对表层的div做一个touchmove的阻止。到达滚动轴底部,向下滑动,阻止事件,向上滑动,开启事件。为此就要判断touchmove的方向了。

javascript 复制代码
var startX ,startY;
$("body").on("touchstart", function(e) {
    e.preventDefault();
    startX = e.originalEvent.changedTouches[0].pageX,
    startY = e.originalEvent.changedTouches[0].pageY;
});
$("body").on("touchmove", function(e) {
    e.preventDefault();
    var moveEndX = e.originalEvent.changedTouches[0].pageX,
    moveEndY = e.originalEvent.changedTouches[0].pageY,
    X = moveEndX - startX,
    Y = moveEndY - startY;
     
    if ( Math.abs(X) > Math.abs(Y) && X > 0 ) {
        alert("left 2 right");
    }
    else if ( Math.abs(X) > Math.abs(Y) && X < 0 ) {
        alert("right 2 left");
    }
    else if ( Math.abs(Y) > Math.abs(X) && Y > 0) {
        alert("top 2 bottom");
    }
    else if ( Math.abs(Y) > Math.abs(X) && Y < 0 ) {
        alert("bottom 2 top");
    }
    else{
        alert("just touch");
    }
});

上面的方法是判断touchmove的滑动方向。

除了上面方法判断手机端手机滑动方向,我这里再介绍一个方案,就是封装一个角度函数,通过角度函数来判断也还不错!我这里仅仅把这种方式实现上滑下滑左滑右滑列举一下!

ini 复制代码
var startx, starty;
    //获得角度
    function getAngle(angx, angy) {
        return Math.atan2(angy, angx) * 180 / Math.PI;
    };
 
    //根据起点终点返回方向 1向上 2向下 3向左 4向右 0未滑动
    function getDirection(startx, starty, endx, endy) {
        var angx = endx - startx;
        var angy = endy - starty;
        var result = 0;
 
        //如果滑动距离太短
        if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
            return result;
        }
 
        var angle = getAngle(angx, angy);
        if (angle >= -135 && angle <= -45) {
            result = 1;
        } else if (angle > 45 && angle < 135) {
            result = 2;
        } else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
            result = 3;
        } else if (angle >= -45 && angle <= 45) {
            result = 4;
        }
 
        return result;
    }
    //手指接触屏幕
    document.addEventListener("touchstart", function(e) {
        startx = e.touches[0].pageX;
        starty = e.touches[0].pageY;
    }, false);
    //手指离开屏幕
    document.addEventListener("touchend", function(e) {
        var endx, endy;
        endx = e.changedTouches[0].pageX;
        endy = e.changedTouches[0].pageY;
        var direction = getDirection(startx, starty, endx, endy);
        switch (direction) {
            case 0:
                alert("未滑动!");
                break;
            case 1:
                alert("向上!")
                break;
            case 2:
                alert("向下!")
                break;
            case 3:
                alert("向左!")
                break;
            case 4:
                alert("向右!")
                break;
            default:
        }
    }, false);

知道滑动方向如何判断,那么解决这个问题我们可以判断是否滑动到底部或者顶部,假如滑动到底部,再往下滑动,就阻止滑动,往上滑动,就开启滑动!滑动到顶部一个道理!总结代码如下:

javascript 复制代码
      $('#haorooms底层背景').bind("touchmove", function (e) {
            e.preventDefault();
        });
        $(".滚动的父亲").bind("touchstart", function (events) {
            startY = events.originalEvent.changedTouches[0].pageY;
        });
        $(".滚动的父亲 ul").bind("touchmove", function (e) {
            var ulheight = $(this).height();
            var scrollTop = $(this).scrollTop();
            var scrollheight = $(this)[0].scrollHeight;
            if (ulheight + scrollTop + 20 >= scrollheight) { //滚到底部20px左右
                $(".滚动的父亲").bind("touchmove", function (event) {
                    moveEndY = event.originalEvent.changedTouches[0].pageY,
                            theY = moveEndY - startY;
                    if (Math.abs(theY) > Math.abs(theX) && theY > 0) { //用上面的abs()更加准确!这里是判断上滑还是下滑!可以用角度函数也可以用上面绝对值方式!
                        $(".滚动的父亲").unbind("touchmove");//滑动到底部再往上滑动,解除阻止!
                    }
                    if (Math.abs(theY) > Math.abs(theX) && theY < 0) {
                        event.preventDefault();//滑动到底部,再往下滑动,阻止滑动!
                    }
                })
            }
            if (scrollTop < 20) {//滚到顶部20px左右
                $(".滚动的父亲").bind("touchmove", function (event) {
                    moveEndY = event.originalEvent.changedTouches[0].pageY,
                            theY = moveEndY - startY;
                    if (Math.abs(theY) > Math.abs(theX) && theY > 0) {
                        event.preventDefault();
                    }
                    if (Math.abs(theY) > Math.abs(theX) && theY < 0) {
                        $(".滚动的父亲").unbind("touchmove");
                    }
                })
            }
        });

以上方法基本上能够阻止body的滚动,但是,有时候还是会有问题,期待更好的解决方案!

张鑫旭的一种解决办法

下面是张鑫旭的一个解决办法,这里我简单的借用一下!

CSS代码:

css 复制代码
.noscroll,
.noscroll body {
    overflow: hidden;
}
.noscroll body {
    position: relative;
}

js代码:

ini 复制代码
$.smartScroll = function(container, selectorScrollable) {
    // 如果没有滚动容器选择器,或者已经绑定了滚动时间,忽略
    if (!selectorScrollable || container.data('isBindScroll')) {
        return;
    }

    // 是否是搓浏览器
    // 自己在这里添加判断和筛选
    var isSBBrowser;

    var data = {
        posY: 0,
        maxscroll: 0
    };

    // 事件处理
    container.on({
        touchstart: function (event) {
            var events = event.touches[0] || event;
            
            // 先求得是不是滚动元素或者滚动元素的子元素
            var elTarget = $(event.target);
            
            if (!elTarget.length) {
                return;    
            }
            
            var elScroll;
            
            // 获取标记的滚动元素,自身或子元素皆可
            if (elTarget.is(selectorScrollable)) {
                elScroll = elTarget;
            } else if ((elScroll = elTarget.parents(selectorScrollable)).length == 0) {
                elScroll = null;
            }
            
            if (!elScroll) {
                return;
            }
            
            // 当前滚动元素标记
            data.elScroll = elScroll;
            
            // 垂直位置标记
            data.posY = events.pageY;
            data.scrollY = elScroll.scrollTop();
            // 是否可以滚动
            data.maxscroll = elScroll[0].scrollHeight - elScroll[0].clientHeight;
        },
        touchmove: function () {
            // 如果不足于滚动,则禁止触发整个窗体元素的滚动
            if (data.maxscroll <= 0 || isSBBrowser) {
                // 禁止滚动
                event.preventDefault();
            }
            // 滚动元素
            var elScroll = data.elScroll;
            // 当前的滚动高度
            var scrollTop = elScroll.scrollTop();
    
            // 现在移动的垂直位置,用来判断是往上移动还是往下
            var events = event.touches[0] || event;
            // 移动距离
            var distanceY = events.pageY - data.posY;
    
            if (isSBBrowser) {
                elScroll.scrollTop(data.scrollY - distanceY);
                elScroll.trigger('scroll');
                return;
            }
    
            // 上下边缘检测
            if (distanceY > 0 && scrollTop == 0) {
                // 往上滑,并且到头
                // 禁止滚动的默认行为
                event.preventDefault();
                return;
            }
    
            // 下边缘检测
            if (distanceY < 0 && (scrollTop + 1 >= data.maxscroll)) {
                // 往下滑,并且到头
                // 禁止滚动的默认行为
                event.preventDefault();
                return;
            }
        },
        touchend: function () {
            data.maxscroll = 0;
        }    
    });

    // 防止多次重复绑定
    container.data('isBindScroll', true);
};

html如下:

css 复制代码
<aside id="aside" class="aside">
    <i class="aside-overlay hideAside"></i>
    <div class="aside-content">
        <div class="module module-filter-list">
            <div class="module-main scrollable">
                <ul id="filters" class="sort-ul">
                    .......
                </ul>
            </div>
        </div>
    </div>
</aside>

使用:

javascript 复制代码
$('#aside').addClass('active');
$.smartScroll($('#aside'), '.scrollable');
$('html').addClass('noscroll');

大家可以测试一下!

相关推荐
咖啡の猫1 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲3 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5814 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路4 小时前
GeoTools 读取影像元数据
前端
ssshooter4 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友4 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry5 小时前
Jetpack Compose 中的状态
前端
dae bal6 小时前
关于RSA和AES加密
前端·vue.js
柳杉6 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog6 小时前
低端设备加载webp ANR
前端·算法