vue彈窗後如何禁止滾動條滾動?

原文地址html


 

常見場景

在許多填寫表單的頁面中,都會彈出一個選擇器,讓你在彈窗中選擇項目。有時,彈窗自己容納不下內容,須要讓它不斷滾動來展現,但由於事件是冒泡的,有時就會形成底部(body 的Z-index確定是在下面的 )的內容也在滾動。這內外一塊兒滾,就有點尷尬了。vue

舉例,下面兩張圖裏,都有相似的問題。那麼怎麼解決呢?ios


 

 

從event. target入手

一種思路就是從事件入手,若是事件對象不是彈窗的話,就讓它什麼都不作。git

(其實這裏有個疑惑,爲何不能經過阻止冒泡來阻止外層的body滾動呢?換了好幾個事件類型都不行,無論是scroll仍是touchmove,很奇怪。
實踐發現,touchmove事件的確不會冒泡了,但body仍然在滾動。。。那我不就不知道事件實現的機制究竟是什麼了,並且用瀏覽器模擬的移動端,也不知道是否是模擬有誤。。。
總之,event.stopPropagation() 方法在這裏不是很管用。。。
)。github

好比圖一中的每一欄滾動條,classname爲listcol,而彈窗爲.listcol,那麼能夠這麼作:web

$('.box--listcol').on('touchmove', function(event) {

				if(event.target.className.indexOf('listcol') === -1){

					event.preventDefault() 
				}
				
			});
// 忽略奇怪的縮進,直接從代碼裏粘貼出來的

仍是直接幹body吧

從event入手雖好,但那樣一來,每個彈窗都要單獨綁一個事件,並且!!!很噁心的是,若是有個反人類的操做,好比內容明明無需滾動,卻綁了事件,就仍然會在滾內容的時候帶動外層滾動。。。。(此時內容是不會滾的,由於高度足夠容納了)。chrome

可能表述的不是很清晰,圖二中,滾動區域的classname 爲 jobscrollcontainer, 彈窗自己爲box–job。瀏覽器

$('.box--job').on('touchmove',function(e){

			if(!$(e.target).parents().filter('.jobscrollcontainer').length){

				e.preventDefault()
			}

		})

正常操做的時候是能夠的。但若是不能滾動硬滾,那就抓瞎了。markdown

因此,仍是要從根子抓起,好比,就固定body不讓它滾!app

function toggleBody(isPin){

	if(isPin){

		document.body.style.height = '100vh'

		document.body.style['overflow-y'] = 'hidden'
	}

	else{

		document.body.style.height = 'unset'

		document.body.style['overflow-y'] = 'auto'

	}
}

toggleBody(1)  //在跳出彈窗的時候
toggleBody(0)  //彈窗消失的時候

超長的頁面怎麼辦呢(2018-11更新)

上面直接限制body當然有效,但若是一個頁面很長很長,超出了100vh,而我正好滾到中間時彈出彈窗。此時若直接限制body的overflow: hidden則會讓頁面一下彈到頂部,顯然不是好的作法。那麼,又該怎麼作呢?

對移動端,能夠引入touch-action,限制爲none,在彈窗消失時再變回auto。但ios的safari上不支持該屬性(能夠去caniuse上查查,起碼2018.11的時候還不支持)。若是咱們的app在ios上用的是safari內核,就起不到效果了。

這時候,就須要結合event.preventDefault屬性來用了。注意在綁定addEventListener的時候,須要多傳一個options,強調這個事件不是passive的,不然谷歌等新版瀏覽器會報錯。同時最好也指定capture: true,這樣能夠早點禁止該事件。

報錯是Unable to preventDefault inside passive event listener due to target being treated as passive.。這是由於谷歌從chrome51以後引入的新優化。事實上,谷歌建議通常狀況下,將 passive 標誌添加到每一個沒有調用 preventDefault() 的 wheel、mousewheel、touchstart 和 touchmove 事件偵聽器。可是,對於這種禁止了默認事件的eventListener,在這種狀況下,反而是要強調它不是消極監聽的。由於滾動都不能滾了,無所謂什麼優化了。

代碼以下(vue版本的):

watch: {
    show(v) {
      this.toggleContainerTouchAction(v)
      if (v) {
        document.body.addEventListener('touchmove', this.stopTouch, { passive: false, capture: true })
      } else {
        document.body.removeEventListener('touchmove', this.stopTouch, { capture: true })
      }
    },
  },
  methods: {
    toggleContainerTouchAction(v) {
      const container = document.querySelector('.container')
      if (!container) {
        return
      }
      container.style['touch-action'] = v ? 'none' : 'auto'
    },
    stopTouch(e) {
      e.preventDefault()
    },

conclusion

直接鎖定body在移動端和web都適用,但若是是從事件入手的話,就要根據須要選擇究竟是用touch 仍是 mousewheel 了。

酌情使用吧,若是一個頁面中有不少不一樣的彈窗,那麼對每個彈窗的蹦出和消失都加上toggleBody事件也是挺煩的。。。也要考慮到用什麼工具作的頁面,若是是框架的話,在組件裏綁會相對簡潔一些。。。


 

返回目錄

相關文章
相關標籤/搜索