通過滑動翻頁效果實現(xiàn)和移動端click事件問題
前述
本文很短~
主要是為了總結(jié)和講述移動端click和js事件機制導(dǎo)致的一個問題。
(:咳咳,其實幾句話就能寫完的還要水一篇文章,不愧是我…
正文
最近做了一個小活動,里面要用到一個效果:滑動翻頁。大概是這樣的:
<!-- HTML代碼 --> <div class="page-container"> <div class="page" style="background: #dc3b26">1</div> <div class="page" style="background: #f3b03d">2</div> <div class="page" style="background: #44a2f8">3</div> </div>
在css中,首先因為是滑動翻頁,所以我們要保證“始終只有一屏”,這個可以放在全局樣式表里控制,然后是其中的“每一頁”都要占滿父元素 —— 這里其實用了“div是塊元素,無外力情況下豎直排列”的特性。
/** css樣式 */ html, body{ margin: 0; padding: 0; width: 100%; height: 100vh; overflow: hidden; font-size: 60px; } .page-container{ width: 100%; height: 100%; } .page-container .page{ width: 100%; height: 100%; }
其JS實現(xiàn)也很簡單:因為在移動端,所以使用了touchstart
、touchmove
及touchend
事件來實現(xiàn)手勢滑動功能:
- start(剛按下)時記錄此時的手指位置——作為初始值;
- 在move(觸摸滑動)時根據(jù)實時的手指位置和初始手指位置變量實現(xiàn)要求判斷,在本文場景為代表的場景下這一步一般還要求出“移動距離”實時賦值;
- 在end(手指離開)時(也有直接在move時進(jìn)行的)進(jìn)行收尾工作——比如:圖片滑動完全劃過去、元素跑到結(jié)束位置、將事件監(jiān)聽取消;
// 這里是控制全局js的文件 // 全局阻止瀏覽器默認(rèn)行為 document.addEventListener("touchstart",function(e){ if(e.cancelable){ e.preventDefault(); } },{passive: false}) // {passive: false}就是告訴前面可能有需要重置行為的代碼 document.addEventListener("touchmove",function(e){ if(e.cancelable){ e.preventDefault(); } },{passive: false})
// JavaScript代碼 let curPageIndex=0; let pageContainer=document.querySelector(".page-container"); let pageNumber=pageContainer.children.length; //頁面的數(shù)量 // 文檔的視窗高度(這里就是一屏的高度) let cHeight=document.documentElement.clientHeight; // 設(shè)置頁面容器的margin-top為合適的值,讓其顯示在視野中 function toPage(){ pageContainer.style.transition="all .5s ease"; pageContainer.style.marginTop=-curPageIndex*cHeight+"px"; // 變化完成后去掉過渡效果 ! pageContainer.addEventListener("transitionend",function(){ pageContainer.style.transition="none"; },{once:true}) } toPage() pageContainer.ontouchstart=function(e){ let y=e.changedTouches[0].clientY; //手指按下的縱坐標(biāo) pageContainer.ontouchmove=function(e){ let dis=e.changedTouches[0].clientY-y; //計算距離 // 計算page-container的margin-top let mtop=-curPageIndex*cHeight+dis if(mtop>0){ mtop=0; }else if(mtop<-(pageNumber-1)*cHeight){ mtop=-(pageNumber-1)*cHeight; } // 實時改變位置 pageContainer.style.marginTop=mtop+"px"; } pageContainer.ontouchend=function(e){ let dis=e.changedTouches[0].clientY-y; // 如果滑動距離實在太短,就回到滑動前的位置狀態(tài) if(Math.abs(dis)<=60){ toPage() }else if(dis>0 && curPageIndex>0){ curPageIndex--; toPage() }else if(dis<0 && curPageIndex<pageNumber-1){ curPageIndex++; toPage() } // 手指離開后,取消監(jiān)聽事件 pageContainer.ontouchmove=null; pageContainer.ontouchend=null; } }
至此,功能上似乎很完美。但這時候,我們在第一個page里添加一個button:
<div class="page" style="background: #dc3b26"> <button onclick="alert('哈哈哈')" class="but">click me!</button> 1 </div>
然后到頁面上查看效果:
無效!
這是因為在上方全局js文件里我們加了“阻止瀏覽器默認(rèn)事件”的代碼。
——在移動端瀏覽器中,click事件和mousestart事件是同時被觸發(fā)的。因為移動端瀏覽器是沒有click事件的,它是由mouse事件模擬的! :也正是這個原因,才有了所謂的“移動端瀏覽器300ms延遲”的問題 1 。
——還有就是,在微信自帶的瀏覽器中,有一個“觸頂下拉回彈”的操作,這其實是不應(yīng)該的。它也屬于瀏覽器默認(rèn)事件。
所以一般我們需要禁止掉這些東西。
但是如上面所示,全部禁止掉總會造成一些困擾,怎么辦?
H5提供了“自定義屬性”,針對本文方法,我們完全可以 —— 在全局事件里檢測當(dāng)前觸發(fā)的元素有沒有某一個自定義屬性,如果有,就什么也不攔截;否則就執(zhí)行禁止默認(rèn)行為的代碼:
比如
<button data-default="true" onclick="alert('哈哈哈')" class="but">click me!</button>
將上面“控制全局js的文件”內(nèi)容改為如下:
// 全局阻止瀏覽器默認(rèn)行為 document.addEventListener("touchstart",function(e){ if(e.target.dataset.default){ return; } if(e.cancelable){ e.preventDefault(); } },{passive: false}) document.addEventListener("touchmove",function(e){ if(e.target.dataset.default){ return; } if(e.cancelable){ e.preventDefault(); } },{passive: false})
就OK了:
其實還有另一種“解法”:既然上面說了,移動端click實際上是通過mouse事件模擬的,那么我們可以從mousestart事件入手;又因為button元素是“第一個頁面”內(nèi)的(子)元素,所以可以用阻止事件冒泡:
<!-- button就是普通的button --> <button class="but">click me!</button>
document.querySelector(".but").addEventListener("touchstart",function(e){ e.stopPropagation(); alert('噶哈哈'); },false)
關(guān)于捕獲和冒泡→
我們首先要知道的是:當(dāng)我們鼠標(biāo)按下一個按鈕時,并不是“點擊了一個按鈕”,而是在這個區(qū)域內(nèi),鼠標(biāo)(上的按鍵)被按下,操作系統(tǒng)和瀏覽器把這個信息對應(yīng)到了“按鈕”所在區(qū)域并觸發(fā)其邏輯。
事實上鼠標(biāo)點擊并沒有位置信息,是操作系統(tǒng)一直在監(jiān)聽鼠標(biāo)移動,根據(jù)累積的位移計算出來的坐標(biāo),將其傳給瀏覽器。
那么,把這個坐標(biāo)轉(zhuǎn)換為具體的元素上的事件的過程,就可稱作“捕獲”。那“冒泡”呢?這個不好解釋,但有一點想必你是明白的:當(dāng)你按下電視開關(guān)時,你也按到了電視!
這就是很多文章會講到的“冒泡過程由內(nèi)向外,捕獲過程由外向內(nèi)”,或者說是“洋蔥模型”。
還有一點就是:事件addEventListener
的第三個參數(shù)true/false
,即為“是捕獲/冒泡”。(別多想,這只是瀏覽器提供的事件模型之一。無論是否監(jiān)聽,在一個事件發(fā)生時,捕獲和冒泡總是先后發(fā)生的)
到此這篇關(guān)于滑動翻頁效果實現(xiàn)和移動端click事件問題的文章就介紹到這了,更多相關(guān)滑動翻頁效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于JavaScript數(shù)組你所不知道的3件事
這篇文章主要為大家詳細(xì)介紹了關(guān)于JavaScript數(shù)組三個并不那么常見的功能,你所不知道的事情,感興趣的小伙伴們可以參考一下2016-08-08在JavaScript中模擬類(class)及類的繼承關(guān)系
眾所周知,JavaScript中沒有類,然而我們卻可以動手實現(xiàn)一個擁有繼承特性的類,所以接下來我們要討論的便是在JavaScript中模擬類(class)及類的繼承關(guān)系:2016-05-05隨機生成10個不重復(fù)的0-100的數(shù)字(實例講解)
下面小編就為大家?guī)硪黄S機生成10個不重復(fù)的0-100的數(shù)字(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08