前幾天項目需求帖蔓,就是常見的移動端頁面矮瘟,點擊出現(xiàn)半透明遮罩,遮罩上面有新彈出的窗體塑娇,窗體內(nèi)部有滾動內(nèi)容芥永,但是會導(dǎo)致遮罩后面的主體頁面也發(fā)生滾動。參考了張鑫旭大神的方法钝吮,自己寫了個相對簡單的,今天把思路整理一下。(因為大神總是考慮的東西比較多奇瘦,而我們就把場景限定在解決這一個問題之下)棘催。
因業(yè)務(wù)代碼不能上傳,同時只要說明白問題就好耳标,頁面就不做過多雕琢啦醇坝。下面是HTML結(jié)構(gòu):
<body>
<p style="border: 1px solid #aaaa55;" id="btn">點擊顯示遮罩</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<i class="shadow"></i>
<div>
<span>確認選擇</span>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li style="margin: 0;">9</li>
</ul>
</div>
</body>
很簡單有木有?一堆P標簽是頁面默認顯示內(nèi)容次坡,第一個P是顯示遮罩的按鈕呼猪。i標簽是半透明遮罩,固定定位寬高100%砸琅,left和top值全為0宋距。div是顯示在遮罩上面的窗體,固定定位在頁面底部症脂,高度比較醒枋辍(小于內(nèi)部ul的高度,我這里是50%)诱篷,overflow設(shè)為auto壶唤。里面主要是ul>li結(jié)構(gòu),展示內(nèi)容棕所;“確認選擇“的span標簽是用來隱藏遮罩和窗體的(點出來咱還得讓它能回去不是闸盔?)。頁面顯示如下:
好琳省,頁面結(jié)構(gòu)大體如圖迎吵,主要是JS部分。我們都知道岛啸,移動端touch事件的默認行為就是頁面的滑動钓觉,所以也就造成了我們彈出的窗體滑到底以后,繼續(xù)下滑會導(dǎo)致頁面內(nèi)容下滑坚踩,滑到頂同樣如此荡灾。那么大體思路就有了(感謝張鑫旭大大):
- 當(dāng)手指touchstart的元素是我們不愿意發(fā)生滾動的時候,阻止默認行為瞬铸;
- 當(dāng)手指touchstart的元素是可滾動的時候批幌,不阻止默認行為;但到達邊緣的時候嗓节,阻止默認行為荧缘;
思路有了,接下來想想該怎么做拦宣。想象一個場景截粗,手指點在彈出層窗體上(touchstart)信姓,向下滑動(touchmove),到達底部即下邊緣(那么怎么判斷到了下邊緣绸罗?)意推,阻止默認事件!向上滑動同理珊蟀,只不過是在到達上邊緣的時候阻止默認事件即可菊值。
整理一下可以發(fā)現(xiàn),我們需要確定的幾個值:
1.邊緣值育灸。
也就是說我們怎么知道什么時候到上邊緣腻窒、什么時候到下邊緣?答案是通過scrollTop磅崭。上邊緣好說儿子,scrollTop為0就行了;那么下邊緣呢绽诚?我們都知道典徊,這個問題的場景就發(fā)生在,一個高度較低的盒子里包裹了一個高度比較高的內(nèi)容恩够。即子元素的高度大于父元素卒落。在我們這里div是那個高度較低(定高50%)的父元素,里面有一個span和一組ul>li結(jié)構(gòu)蜂桶。這里就需要用到兩個概念儡毕,一個是clientHeight,一個是scrollHeight扑媚。前者是視口大小腰湾,就是div能顯示的高度(其實就是我們這里設(shè)置的50%);而scrollHeight則是滾動大小疆股,指的是包含滾動內(nèi)容的元素大蟹逊弧(元素內(nèi)容的總高度)。仔細想想就能知道旬痹,用總的內(nèi)容高減去視口的高度附井,剩下的就是到達“內(nèi)容底部”所需要“走過“的距離,也就是說两残,當(dāng)我們滾動距離達到這個值時永毅,就“到底”了,即達到下邊緣人弓。
2.確定方向沼死。
當(dāng)觸摸到彈出窗體時(touchstart),記錄垂直位置startY崔赌;當(dāng)在彈出窗體上滑動時(touchmove)意蛀,記錄當(dāng)前垂直位置endY耸别,這兩個值的差可用來確定頁面方向。如果頁面向上滾(在這里把能到達上邊緣定義為“向上滾”)浸间,手指是向下的太雨,則endY減去startY大于0;如果頁面向下滾(即能到達下邊緣)魁蒜,手指是向上的,則endY減去startY小于0吩翻。如此兜看,方向可以確定。
說了這么多狭瞎,干貨來了细移,JS代碼如下(需要引入jQuery):
<script>
let init = {
data: { //數(shù)據(jù)初始化
startY: 0, //觸摸到窗體時垂直位置標記(touchstart)
startScrollTop: 0, //每次觸摸窗體時的滾動距離(touchstart)
endScrollTop: 0, //元素當(dāng)前的滾動距離(touchmove)
maxScroll: 0 //下邊緣距離(元素滾動maxScroll就到底啦)
},
//touchstart發(fā)生的事件
begin: function (event) {
init.data.startY = event.targetTouches[0].pageY;
init.data.startScrollTop = event.currentTarget.scrollTop;
init.data.maxScroll = this.scrollHeight - this.clientHeight; //下邊緣距離(元素滾動maxScroll就到底啦)
$(this).on('touchmove', init.move);
},
//touchmove發(fā)生的事件
move: function (event) {
let endScrollTop = this.scrollTop;
let endY = event.targetTouches[0].pageY; //元素當(dāng)前的垂直位置標記,與觸摸到窗體時垂直位置標記相差來確定向上滑還是向下滑
let direction = endY - init.data.startY; //如上所述熊锭,確定方向弧轧。
if(direction > 0 && endScrollTop === 0){
init.zuzhi(); //向上滑并且當(dāng)前的滾動距離為0,說明到達上邊緣碗殷,阻止默認事件
}
if(direction < 0 && endScrollTop + 1 >= init.data.maxScroll){
init.zuzhi(); //向下滑并且當(dāng)前的滾動距離即將達到endScrollTop精绎,說明即將到達下邊緣,阻止默認事件
}
$(this).on('touchend', init.end);
},
end: function () {
init.data.maxScroll = 0;
},
zuzhi: function (e) {
event.preventDefault();
return;
},
// 通過切換類名來控制遮罩及彈窗的顯示隱藏
shadowToggle: function (ele, myclassName) {
$(ele).hasClass(myclassName) ? $(ele).removeClass(myclassName) : $(ele).addClass(myclassName);
},
};
$('div').on('touchstart', init.begin);
$('.shadow').on('touchmove', init.zuzhi);
$('#btn').on('click', function () {
init.shadowToggle('.shadow', 'show');
init.shadowToggle('html', 'noscroll');
init.shadowToggle('div', 'show');
});
$('span').click(function () {
init.shadowToggle('.shadow', 'show');
init.shadowToggle('html', 'noscroll');
init.shadowToggle('div', 'show');
})
</script>
此方案在iPhone7Plus锌妻、iPhone6s及小米5代乃、魅族魅藍note5上測試通過。水平所限仿粹,有什么疏漏還請大家不吝賜教搁吓,期待共同進步!