移動端彈出層背景禁止滾動

發頁面上某個元素或者達到某個條件時,頁面彈出模態框的場景應該是很常見的了,特別是在屏幕較小的移動端,例以下面這種:javascript

這裏寫圖片描述

對於這個效果,以前一直都沒怎麼在乎探究過,由於以爲應該沒什麼好弄的,直到,我接到了一個包含此效果的需求以後,我才知道什麼叫眼高手低,仍是太年輕。css


body: overflow:hidden

第一次嘗試這個效果的時候,我稍稍思考了一下,以爲給 body加個 overflow:hidden;height: 100vh;的樣式應該就能夠了,因此就這麼寫了。html

DOM以下:java

// HTML
<div class="box"> <div class="box1"> </div> </div>

當彈出層出現時,給 body設置樣式:node

document.body.style.height = '100%' document.body.style.overflow = 'hidden'

寫完後順便在個人幾個瀏覽器上試了一下,桌面瀏覽器模擬移動端的那種,效果槓槓滴,和我預想的同樣,沒毛病。chrome

but,哪有這麼簡單的事情?瀏覽器

桌面瀏覽器是 ok了,可是這個項目的主要場景是 移動端,因此要經過真正的移動端瀏覽器才行,桌面瀏覽器模擬的可不算數,結果移動端瀏覽器中,除了 移動chrome以外,全跪了,給 body加上那兩個樣式和沒加的效果是同樣的,背景層改怎麼滾動還怎麼滾動。緩存


body+html: overflow:hidden

百思不得其解之下,只好跑到網上找了一圈,結果然讓我又找到了一個答案,說是僅僅給 body設置 overflow是不行的,還必須同時給 html節點也加上這個樣式才行,因而就試了一下。函數

document.documentElement.style.height = '100%' document.documentElement.style.overflow = 'hidden' document.body.style.height = '100%' document.body.style.overflow = 'hidden'

桌面瀏覽器模擬移動端測試經過,以前跪了的移動瀏覽器如今也都 ok了,給這兩個元素加上上述樣式後,彈出層背景 body肯定是不會滾動了。測試

but,又出現了另一個問題,當將頁面往下滾動一段距離,也就是說 document.body.scrollTop 大於 0時,再顯示彈出層,增長上述四行代碼時,頁面自動滾到了最頂部,也就是說瀏覽器像是自動執行了這一行代碼 document.body.scrollTop=0

仔細想一想也是,以前頁面是超出一個屏幕高度的,因此能夠滾動,可是如今你把頁面高度設爲一個屏幕高度 100%,而且 overflow:hidden,那麼根據 overflow:hidden的特性,瀏覽器確定是要從頁面的頭部開始截取一個屏幕的高度,剩下的再 hidden

若是彈出層時,背景是徹底看不到的,一片漆黑,也就是相似 rgba(0,0,0,1),而不是半透明 rgba(0,0,0,.6),那麼實際無傷大雅,也就是一行代碼的事情。

在彈出層彈出以前,先保存此事頁面的 scrollTop,而後在彈出層關閉的時候,再將頁面的 scrollTop設定到以前保存的那個位置,因此這樣最起碼看起來背景是沒變的,就像下面這樣:

var box1 = $('.box1') var body= document.body var scrolltop body.addEventListener('click', function() { if (box1.className.indexOf('hidden') !== -1) { // 保存頁面滾動到的位置 scrolltop = document.body.scrollTop body.style.height = '100%' body.style.overflow = 'hidden' document.documentElement.style.height = '100%' document.documentElement.style.overflow = 'hidden' // 顯示彈出層 box1.className ='box1 show' } else { // 隱藏彈出層 box1.className ='box1 hidden' body.style.height = 'auto' body.style.overflow = 'visible' document.documentElement.style.height = 'auto' document.documentElement.style.overflow = 'visible' // 恢復頁面滾動到的位置 document.body.scrollTop = scrolltop } })

若是背景層是可見的呢,只要用戶不瞎,確定能看到頁面發生跳動了啊。


JS控制

  • 區別對待

看來只經過 css來完成這個效果是有些難度了,因而將主意打到了 js上,以下:

box1.addEventListener('touchmove', function(e){ e.preventDefault() })

 

直接禁止彈出層的掉滾動事件,由於彈出層是滿屏覆蓋在 頁面上的,並且這個事件也沒有 點透,因此確實是達到了禁止背景 頁面滾動的效果。

but,背景元素的滾動是禁止掉了,但這種禁止幾乎是把頁面上全部元素的滾動事件都禁止掉了,若是在彈出層元素 box1中存在能夠滾動的元素,那麼一樣也會被禁止滾動,這可不是咱們想要的,因此必需要把彈出層內的元素排除在外才行。

彈出層內可滾動區域包括可滾動的頂級元素,以及此元素下全部的子元素,因此只要判斷當前正在滾動的區域是此區域內的元素,則容許滾動,因此這裏須要判斷當前 touch的元素是否是彈出層內能夠滾動的元素,以及是否是其子元素,判斷是否爲其子元素只須要一個循環遞歸便可,例如如下代碼:

function getRecursiveEle(ele, parentClassName) { if (ele.className.indexOf(parentClassName) !== -1) { return ele } else { if (ele.nodeName.toLowerCase() === 'body') { return null } ele = ele.parentNode return getRecursiveEle(ele, parentClassName) } }

調用此函數,傳入 e.target以及彈出層能夠滾動的元素類名便可,返 回 true,則代表是能夠滾動的元素。

but,雖然你直接禁止了彈出層可滾動元素其外的元素滾動,然而同時又容許彈出層內可滾動元素滾動,那麼當將滾動元素滾動到頭的時候,背景仍是會滾動。

因此,還須要加一個判斷,當滾動元素滾動到頭或者尾部的時候,再禁止全部元素滾動,這裏的滾動到頭包括兩種狀況,到頭和到尾。

// 滾動到頭的狀況,其中touchstartY 爲開始滾動時接觸點的 `pageY`
if(modal.scrollTop <= 0) { e.targetTouches[0].pageY > touchstartY && e.preventDefault() }
// 滾動到尾的狀況,其中touchstartY 爲開始滾動時接觸點的 `pageY`,itemH爲可滾動元素框內部的子元素總高度,+2是由於邊界問題 (itemH - modal.offsetHeight < modal.scrollTop + 2) && (e.targetTouches[0].pageY < touchstartY) && e.preventDefault()

這樣的話,問題大致上解決了,但還有點小問題,在有的瀏覽器上,當可滾動元素滾動到頭的時候,背景依舊仍是會稍微滾動一點距離,不太完美。

  • 另想他法

依舊讓覆蓋整個屏幕的彈出層禁止滾動,彈出層內部可滾動元素的滾動經過 js來控制,例如使用 translate控制上下滾動距離。

// touchstartY 爲touchstart事件發生時的 e.targetTouches[0].pageY var translateEndY = 0, translateEndYTemp = 0 box1.addEventListener('touchmove', function(e) { // 禁止默認滾動 e.preventDefault() translateEndYTemp = e.targetTouches[0].pageY-touchstartY + translateEndY // 經過改變 translate來滾動元素 $('.item').style.transform = 'translate(0, '+translateEndYTemp+'px)' }) // 緩存下每次滾動過的距離 box1.addEventListener('touchend', function(e) { translateEndY = translateEndYTemp })

嗯,這樣就差很少了,不過由於滾動時經過 translate實現的,因此滾動元素是不受父元素約束的,也就是說滾動元素會滾過界,這個很好解決,在 touchend的時候,判斷一下有沒有過界,若是過界了反彈回來就行

相關文章
相關標籤/搜索