還在用定時器嗎?借助 CSS 來監(jiān)聽事件
平時工作中很多場合都要用到定時器,比如延遲加載、定時查詢等等,但定時器的控制有時候會有些許麻煩,比如鼠標移入停止、移出再重新開始。這次介紹幾個借助 CSS 來更好的控制定時器的方法,一起了解一下吧,相信可以帶來不一樣的體驗。
一、hover 延時觸發(fā)
有這樣一個場景,在鼠標停留在一個元素上1s?后才觸發(fā)事件,不滿1s?就不會觸發(fā),這樣的好處是,可以避免鼠標在快速劃過時,頻繁的觸發(fā)事件。如果是用js來實現(xiàn),可能會這樣。
var timer = null
el.addEventListener('mouseover', () => {
timer && clearTimeout(timer)
timer = setTimeout(() => {
// 具體邏輯
}, 1000)
})
是不是這樣?等等,這樣還沒完,這樣只做到了延時,鼠標離開以后還是會觸發(fā),還需要在鼠標離開時取消定時器。
el.addEventListener('mouseout', () => {
timer && clearTimeout(timer)
})
另外,在使用mouseout?時還需要考慮 dom? 嵌套結(jié)構(gòu),因為這些事件在父級 -> 子級的過程中仍然會觸發(fā),總之,細節(jié)會非常多,很容易誤觸發(fā)。
現(xiàn)在轉(zhuǎn)折來了,如果借用 CSS 就可以有效地避免上述問題,如下,先給需要觸發(fā)的元素加一個有延時的transition。
button:hover{
opacity: 0.999; /*無關(guān)緊要的樣式*/
transition: 0s 1s opacity; /*延時 1s */
}
這里只需一個無關(guān)緊要的樣式就行,如果opacity?已經(jīng)使用過了,可以使用其他的,比如transform:translateZ(.1px)?,也是可行的。然后添加監(jiān)聽transitionend方法。
GlobalEventHandlers.ontransitionend - Web API 接口參考 | MDN (mozilla.org)[1]。
el.addEventListener('transitionend', () => {
// 具體邏輯
})
這就結(jié)束了。無需定時器,也無需取消,更無需考慮 dom 結(jié)構(gòu),完美實現(xiàn)。
下面是一個小實例,在hover?一段時間后觸發(fā)alert。
Kapture 2022-09-11 at 15.56.37
原理和上面一致,完整代碼可以查看線上demo:hover_alert (codepen.io)[2]或者hover_alert(runjs.work)[3]。
以后再碰到這樣的需要可以停下來思考一番,很多和mouseover有關(guān)的交互都可以用這種方式來實現(xiàn)。
二、長按觸發(fā)事件
長按也是一個比較常見的需求,它可以很好的和點擊事件區(qū)分開來,從而賦予更多的交互能力。
但是原生js中卻沒有這樣一個事件,如果要實現(xiàn)長按事件,通常需要借助定時器和鼠標按下事件,如下:
el.onmousedown = function(){
this.timer && clearTimeout(this.timer);
this.timer = settimeout(function(){
//業(yè)務(wù)代碼
},1000)
}
el.onmouseup = function(){
this.timer && clearTimeout(this.timer);
}
又是定時器和取消定時器的場景,和前面一個例子有些類似,也可以借助 CSS 來實現(xiàn),由于是鼠標按下,可以聯(lián)想到:active,因此可以這樣來實現(xiàn)。
button:hover:active{
opacity: .999; /*無關(guān)緊要的樣式*/
transition: opacity 1s; /*延時 1s */
}
然后再監(jiān)聽transitionend方法;
el.addEventListener('transitionend', () => {
// 具體邏輯
})
是不是非常方便呢?下面是以前做過的一個小案例,實現(xiàn)了長按觸發(fā)元素選中。
Kapture 2022-09-13 at 10.37.01
完整代碼可以查看線上demo:長按框選 (codepen.io)[4]或者長按框選 (runjs.work)[5]。
三、輪播和暫停
再來看一個比較有意思的例子,輪播圖。
通常輪播圖都會自動播放,然后鼠標hover時會暫停輪播圖,通常的做法是這樣的。
function autoPlay(){
timer && clearInterval(timer)
timer = setInterval(function(){
// 輪播邏輯
}, 1000)
}
autoPlay()
view.onmouseover = function(){
timer && clearInterval(timer)
}
el.onmouseout = function(){
autoPlay()
}
又是定時器的取消和設(shè)置,要綁定一堆事件,太煩人了,可以換種方式嗎?當然可以了,借助 CSS 動畫,一切都好辦了。
和前面不太相同的是,這里是setInterval?,可以重復(fù)觸發(fā),那 CSS 中有什么可以重復(fù)觸發(fā)的呢?沒錯,就是 CSS 動畫!當 CSS 動畫設(shè)置次數(shù)為infinite?就可以無限循環(huán)了,和這個定時器效果非常類似,而且可以直接通過:hover?暫停和播放動畫。監(jiān)聽每次動畫的觸發(fā)可以用animationiteration這個方法,表示每個動畫輪回就觸發(fā)一次。
GlobalEventHandlers.onanimationiteration - Web API 接口參考 | MDN (mozilla.org)[6]。
所以用這種思路實現(xiàn)就是;
.view {
animation: scroll 1s infinite; /*每1s動畫,無限循環(huán)*/
}
.view:hover{
animation-play-state: paused; /*hover暫停*/
}
@keyframes scroll {
to {
transform: translateZ(.1px); /*無關(guān)緊要的樣式*/
}
}
然后再監(jiān)聽animationiteration事件;
view.addEventListener("animationiteration", () => {
// 輪播邏輯
})
是不是省去了大半的js代碼?而且也更好理解,控制也更為方便。
下面是一個通過animationiteration?來代替setInterval實現(xiàn)的輪播圖。
Kapture 2022-09-11 at 16.43.49
完整代碼可以查看線上demo:CSS banner(codepen.io)[7]或者css_banner(runjs.work)[8]。
四、總結(jié)一下
以上就是你可能不需要定時器的幾個替代方案,相比定時器而言,CSS 在控制定時器的開啟和暫停上更有優(yōu)勢,下面總結(jié)一下:
- :hover?配合transition?延時、transitionend監(jiān)聽可以實現(xiàn)鼠標經(jīng)過延時觸發(fā)效果。
- :active?配合transition?延時、transitionend監(jiān)聽可以實現(xiàn)長按觸發(fā)效果。
- CSS 動畫設(shè)置infinite?后配合animationiteration監(jiān)聽可以實現(xiàn)周期性觸發(fā)效果。
- 可以直接通過:hover來控制臺動畫的暫停和播放。
當然,可以利用的不僅僅是以上幾個案例,任何和 CSS 交互(:hover、:active)有類似功能的都可以朝這個方向去思考,是不是可以實現(xiàn)地更加優(yōu)雅?