最近學習了一下vue.js。對這一框架的最深入印象就是vue的組件化思想和數據驅動視圖而不直接操做DOM的思想。在JQuery逐漸黯淡的前端發展趨勢下,這種UI狀態的變化操做方式勢必會成爲主流。那麼在此以一個小例子做爲說明。前端
建立一個按鈕,按下一次就能夠清除單次輸入(或者持續按住能夠清除全部輸入),即按下(或者按住)一個按鈕時,既執行一個函數,又清除輸入。
vue
首先會始用JS實現,再用vue實現,以便對比突出vue特色express
原理框架
要實現長按,用戶須要按下並按住按鈕幾秒鐘。ide
想經過代碼模擬這一效果,咱們須要在鼠標「點擊」按下按鈕時,啓動一個計時器監聽用戶按下的時長,若是時間超過咱們指望的時長,就執行相應的函數。函數
很是簡單!然而,咱們須要知道用戶什麼時候按住按鈕。
組件化
如何實現
學習
當用戶點擊按鈕時,在點擊事件以前會觸發另外兩個事件: mousedown 和 mouseup。
this
當用戶按下按鈕時觸發 mousedown 事件,用戶鬆開按鈕時調用 mouseup 事件。
component
咱們須要作的是:
mousedown 事件觸發時,啓動計時器。
一旦 mouseup 事件在預期的 2 秒前被觸發,就清除計時器,不要執行相應的函數。就看成一個普通的點擊事件。
只要計時器在咱們預設的時間內沒有被清除,即 mouseup 事件沒有被觸發——那麼能夠判定用戶沒有釋放按鈕。所以,能夠斷定爲一次長按,能夠執行關聯的函數。
實踐
讓咱們深刻代碼,完成這一功能。
首先,咱們必須定義三件事,即:
一個 變量 用於存儲計時器。
一個 啓動 功能函數,用於啓動計時器。
一個 取消 功能函數,用於取消計時器。
變量
這個變量主要用來保存 setTimeout 的值,以便當鼠標 mouseup 事件觸發時咱們能夠取消它。
let pressTimer = null;
咱們把變量值設置爲 null 是爲了在執行取消操做前,檢查這個變量的值判斷當前是否有一個正在運行的計時器。
啓動函數
這個函數包括一個 setTimeout,它是 JavaScript 中的一個基本方法,容許在特定時間以後執行一個函數。
注意,click 事件執行的過程當中,會觸發另外兩個事件。可是咱們須要啓動計時器的是 mousedown 事件。若是隻是點擊事件,不須要啓動計時器。
// 建立計時器 ( 1s以後執行函數 )
let start = (e) => {
// 若是是點擊事件,不啓動計時器
if (e.type === 'click' && e.button !== 0) {
return;
}
// 在啓動一個定時器以前確保沒有正在運行的計時器
if (pressTimer === null) {
pressTimer = setTimeout(() => {
// 執行任務 !!!
}, 1000)
}
}
取消函數
這個函數見名知意,用來取消啓動函數建立的 setTimeout。
要取消 setTimeout ,能夠使用 JavaScript 中的 clearTimeout 方法,它主要用來清除 setTimeout() 方法設置的計時器。
在使用 clearTimeout 以前,須要檢查 pressTimer 變量是否爲 null。若是沒有爲 null,意味着有一個正在運行的計時器。所以,咱們須要先清除它,而且將 pressTimer 變量設置爲 null。
let cancel = (e) => {
// 檢查 pressTimer 的值是否爲 null
if (pressTimer !== null) {
clearTimeout(pressTimer)
pressTimer = null
}
}
一旦 mouseup 事件觸發,這個函數就會被調用。
設置觸發器
剩下的就是將事件監聽器添加到想要長按效果的按鈕上。
addEventListener("mousedown", start);
addEventListener("click", cancel);
以上代碼合到一塊兒是這樣:
// 定義變量
let pressTimer = null;
// 建立計時器( 1秒後執行函數 )
let start = (e) => {
if (e.type === 'click' && e.button !== 0) {
return;
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
// 執行任務 !!!
}, 1000)
}
}
// 中止計時器
let cancel = (e) => {
// 檢查是否有正在運行的計時器
if ( pressTimer !== null ) {
clearTimeout(pressTimer);
pressTimer = null;
}
}
// 選擇 id 爲 longPressButton 的元素
let el = document.getElementById('longPressButton');
// 添加事件監聽器
el.addEventListener("mousedown", start);
// 長按事件取消,取消計時器
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
用 Vue 指令包裝
建立 Vue 指令時,能夠建立全局或局部指令,本文中,咱們採用全局指令。
首先,咱們必須聲明自定義指令的名稱。
Vue.directive('longpress', {
})
這就註冊了一個名爲 v-longpress 的全局自定義指令。
接下來,咱們添加帶參數的 bind 鉤子函數,它容許咱們引用指令綁定的元素,獲取傳遞給指令的值,並標識指令使用的組件。
Vue.directive('longpress', {
bind: function(el, binding, vNode) {
}
})
接下來,咱們在 bind 函數中添加長按功能的代碼。
Vue.directive('longpress', {
bind: function(el, binding, vNode) {
// 定義變量
let pressTimer = null;
// 定義函數處理程序
// 建立計時器( 1秒後執行函數 )
let start = (e) => {
if (e.type === 'click' && e.button !== 0) {
return;
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
// 執行任務 !!!
}, 1000)
}
}
// 取消計時器
let cancel = (e) => {
// 檢查是否有正在運行的計時器
if ( pressTimer !== null ) {
clearTimeout(pressTimer);
pressTimer = null;
}
}
// 添加事件監聽器
el.addEventListener("mousedown", start);
// 取消計時器
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
}
})
接下來,咱們須要添加一個函數來運行傳遞給 longpress 指令的方法。
Vue.directive('longpress', {
bind: function(el, binding, vNode) {
// 定義變量
let pressTimer = null;
// 定義函數處理程序
// 建立計時器( 1秒後執行函數 )
let start = (e) => {
if (e.type === 'click' && e.button !== 0) {
return;
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
// 執行函數
handler();
}, 1000)
}
}
// 中止計時器
let cancel = (e) => {
// 檢查是否有正在運行的計時器
if ( pressTimer !== null ) {
clearTimeout(pressTimer);
pressTimer = null;
}
}
// 運行函數
const handler = (e) => {
// 執行傳遞給指令的方法
binding.value(e)
}
// 添加事件監聽器
el.addEventListener("mousedown", start);
// 取消計時器
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
}
})
如今,能夠在 Vue 應用中使用這個指令了,除非使用者給指令傳入的值不是一個函數。所以,咱們須要經過警告反饋給使用者。
爲了反饋給使用者,咱們在 bind 函數中添加了如下內容:
// 確保提供的表達式是函數
if (typeof binding.value !== 'function') {
// 獲取組件名稱
const compName = vNode.context.name;
// 將警告傳遞給控制檯
let warn = `[longpress:] provided expression '${binding.expression}' is not a function,but has to be `;
if (compName) { warn += `Found in component '${compName}' ` }
console.warn(warn);
}
最後,若是這個指令也適用於觸屏設備,那會是極好的。所以,咱們添加了 touchstart、touchend 和 touchcancel 事件監聽器。
最終代碼以下:
Vue.directive('longpress', {
bind: function(el, binding, vNode) {
// 確保提供的表達式是函數
if (typeof binding.value !== 'function') {
// 獲取組件名稱
const compName = vNode.context.name;
// 將警告傳遞給控制檯
let warn = `[longpress:] provided expression '${binding.expression}' is not afunction, but has to be `;
if (compName) { warn += `Found in component '${compName}' `}
console.warn(warn);
}
// 定義變量
let pressTimer = null;
// 定義函數處理程序
// 建立計時器( 1秒後執行函數 )
let start = (e) => {
if (e.type === 'click' && e.button !== 0) {
return;
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
// 執行函數
handler();
}, 1000)
}
}
// 取消計時器
let cancel = (e) => {
// 檢查計時器是否有值
if ( pressTimer !== null ) {
clearTimeout(pressTimer);
pressTimer = null;
}
}
// 運行函數
const handler = (e) => {
// 執行傳遞給指令的方法
binding.value(e)
}
// 添加事件監聽器
el.addEventListener("mousedown", start);
el.addEventListener("touchstart", start);
// 取消計時器
el.addEventListener("click", cancel);
el.addEventListener("mouseout", cancel);
el.addEventListener("touchend", cancel);
el.addEventListener("touchcancel", cancel);
}
})
如今能夠在 Vue 組件裏使用了:
<template>
<div>
<button v-longpress="incrementPlusTen" @click="incrementPlusOne">{{value}}</button>
</div>
</template>
<script>
export default {
data() {
return {
value: 10
}
},
methods: {
// 增長1
incrementPlusOne() {
this.value++
},
// 增長10
incrementPlusTen() {
this.value += 10
}
}
}
</script>