如何真正優(yōu)雅解決按鈕”重復(fù)點擊“問題?
關(guān)注公眾號?前端人,回復(fù)“加群”
添加無廣告優(yōu)質(zhì)學(xué)習(xí)群
一、對思否的一點吐槽
思否上的許多按鈕都沒有做重復(fù)點擊檢測的問題,往往導(dǎo)致點擊沒反應(yīng),多次點擊后突然發(fā)表多條相同內(nèi)容,比如和一位同學(xué)的私信:

又如一個問題下的評論:

如果你在這篇文章下發(fā)表評論,可以不小心多點幾下"提交評論"按鈕,會發(fā)現(xiàn)也存在相同的問題~~~
這個問題怎么解決呢?
簡單點,使用一個lock標(biāo)記,在請求發(fā)出時上鎖,上鎖后就不可以再發(fā)請求,可以在請求結(jié)束后解鎖:
let?clickButton?=?(function?()?{
??let?lock?=?false
??return?function?(postParams)?{
????if?(lock)?return
????lock?=?true
????//?假設(shè)使用axios發(fā)送請求
????axios.post('urlxxx',?postParams).then(
??????//?表單提交成功
????).catch(error?=>?{
??????//?表單提交出錯
??????console.log(error)
????}).finally(()?=>?{
??????//?不管成功失敗?都解鎖
??????lock?=?false
????})
??}
})()
button.addEventListener('click',?clickButton)
當(dāng)然對于button按鈕,可以使用setAttribute('disabled', xxx)和removeAttribute('disabled')來代替lock標(biāo)記。
這個方案問題在于,對于每一次按鈕點擊,我們都要寫個lock標(biāo)記,相當(dāng)于重復(fù)的邏輯會出現(xiàn)在代碼的各個地方——是不是可以封裝一下呢?
二、封裝按鈕鎖定、解鎖邏輯
寫一個裝飾器將邏輯封裝起來:
function?ignoreMultiClick(func,?manual?=?false)?{
??let?lock?=?false
??return?function?(...args)?{
????if?(lock)?return
????lock?=?true
????let?done?=?()?=>?(lock?=?false)
????if?(manual)?return?func.call(this,?...args,?done)
????let?promise?=?func.call(this,?...args)
????Promise.resolve(promise).finally(done)
????return?promise
??}
}
將想監(jiān)聽點擊回調(diào)函數(shù)func作為傳遞給ignoreMultiClick進行裝飾,會返回一個新的函數(shù),使用該函數(shù)作為點擊的回調(diào)事件即可。
有兩種方法解鎖:
手動解鎖:
可以給ignoreMultiClick傳遞一個參數(shù)manual,意思是主動調(diào)用解鎖。
若該參數(shù)為truthy,則點擊事件觸發(fā)時會給原始的點擊回調(diào)func傳遞一個參數(shù)done,done是一個函數(shù),調(diào)用它可以解鎖。
自動解鎖:
可以使原監(jiān)聽函數(shù)func返回一個promise,在該promise決議后自動執(zhí)行解鎖操作。
因為Promise管理回調(diào)函數(shù)非常方便,并且像axios這樣非常常用的請求庫返回值本身也是一個promise,所以默認(rèn)情況使用這種方式。
當(dāng)然返回promise并不是必須的,有時候我們在發(fā)請求前會進行一些驗證,驗證沒通過則直接return,此時裝飾器函數(shù)也能正常處理,因為使用Promise.resolve包裹了一下promise:Promise.resolve(promise).finally(done)。
三、使用實例
自動解鎖使用例子:
let?clickButton?=?ignoreMultiClick(function?(postParams)?{
??//?假設(shè)有一些檢測表單的操作,檢查不通過則直接返回
??if?(!checkForm()){
???return?done()
??}???
??//?返回promise
??return?axios.post('urlxxx',?postParams).then(
????//?表單提交成功
??).catch(error?=>?{
????//?表單提交出錯
????console.log(error)
??})
})
button.addEventListener('click',?clickButton)
手動解鎖:
let?clickButton?=?ignoreMultiClick(function?(postParams,?done)?{
??//?表單驗證不通過解鎖
??if?(!checkForm()){
???return?done()
??}??
??axios.post('urlxxx',?postParams).then(
????//?表單提交成功
??).catch(error?=>?{
????//?表單提交出錯
????console.log(error)
??}).finally(()?=>?done())?//?請求結(jié)束解鎖
})
button.addEventListener('click',?clickButton)
普通場景下還是自動解鎖比較簡單,因為可能有多個條件分支,手動解鎖需要在每一個返回的地方都調(diào)用done。
原文:segmentfault.com/a/1190000038291773

1.你的點贊、在看 是對【鬼哥】最大的支持,一鍵三連吧
2.關(guān)注公眾號前端人,回復(fù)資料包領(lǐng)取我整理的前端進階資料包
3.回復(fù)加群,加入前端進階群,和小伙伴一起學(xué)習(xí)討論!
