0%

JS 用同步的方式實現事件監聽

前言

沒事多看看網路面試題 , 多看看沒事

實作連結(可以點開F12看功能)


在網路上看到有面試題 ,題目如下:

1
2
3
4
5
6
7
8
9
10
11
12
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
}

(async()=>{
const btn = getElement('button');
while (1){
await btn.waitClick;
console.log('按鈕被點擊了!!')
}
})();

看一下需求大概是 調用getElement()函數,傳入一個 CSS 選擇器 後拿到一個元素,

拿到元素後就可以給這個元素添加事件 來等待他點擊

然後點擊後, 在進入循環 等待下一次點擊

跟普通使用的 onclick事件很像

差別看起來是用這種方式,就沒有任何回調的東西傳回來

是以一種同步的方式來書寫異步的代碼!!!


實現思路

1
2
3
4
5
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
return dom;
}

這時候會發現 他一直循環 -..-+

看起來還有點問題

發現是 await btn.waitClick 裡面的 btn.waitClick 是undefined 他找不到東西,就一直循環!!!
(其實就等於 promise(resolve))

1
await promise(resolve);

所以要在 function 內加上 btn.waitClick 屬性

1
2
3
4
5
6
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
btn.waitClick = new Promise();
return dom;
}

這樣就完成可以實現等待點擊的效果。

但是這種寫法有一種缺點是 不能確定每一次都是 click 事件

搞不好有 mouseoverchange之類的事件

不一定都是click所以可以改成

1
2
3
4
5
6
7
8
9
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
btn.waitClick = new Promise();
btn.waitMouseover =new Promise();
//...以此類推

return dom;
}

但這樣寫法就要每個事件都寫一次 !!!太累了

換個想法

如果不這樣寫,改成換來看await 後面是以wait開頭,就來看看後面的詞是就是我要做的屬性

1
2
3
4
5
6
7
8
9
10
11
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
const domProxy = new Proxy(dom,{
get(target,key){
console.log(key);
}
})

return domProxy;
}

這樣就可以看到 btn 後面的文字是什麼了

接下來看一下是不是已 wait 開頭的文字選項

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
const domProxy = new Proxy(dom,{
get(target,key){
// console.log(key);
if (!key.startsWith('wait')){
return target[key];
};
const even = key.replace('wait','').toLowerCase(); //事件的名字
console.log(even);
}
})

return domProxy;
}

這樣寫法 就可以顯示其他已 wait 開頭的功能

然後要寫 click 的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
const domProxy = new Proxy(dom,{
get(target,key){
// console.log(key);
if (!key.startsWith('wait')){
return target[key];
};
const even = key.replace('wait','').toLowerCase(); //事件的名字
// console.log(even);
return new Promise((resolve)=>{
target.addEventListener(even,resolve,{once:true}) //監聽事件,然後調用resolve ,只做一次!
})
}
})

return domProxy;
}

上面代表 監聽這個事件,然後調用resolve ,只發生一次

因為代碼是從上到下,不可能反覆監聽,所以要加上只做一次!!

如果需要多次監聽就只要在使用這個Function時,寫上循環即可。

1
2
3
4
5
6
7
8
(async()=>{
const btn = getElement('button');
while (1){
await btn.waitClick;
console.log('按鈕被點擊了!!')
}
// console.log(btn.clientWidth)
})();

完整的代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function getElement(cssSelector){
//請完成getElement函數,讓後續的程式順利執行
const dom = document.querySelector(cssSelector);
const domProxy = new Proxy(dom,{
get(target,key){
// console.log(key);
if (!key.startsWith('wait')){
return target[key];
};
const even = key.replace('wait','').toLowerCase(); //事件的名字
// console.log(even);
return new Promise((resolve)=>{
target.addEventListener(even,resolve,{once:true}) //監聽事件,然後調用resolve ,只做一次!
})
}
})

return domProxy;
}

(async()=>{
const btn = getElement('button');
while (1){
await btn.waitClick;
console.log('按鈕被點擊了!!')
}
})();

如果想要限制點擊次數 例如:10次
只要把循環的方式改成 for 循環 10次 如下:

1
2
3
4
5
6
7
(async()=>{
const btn = getElement('button');
for (let i = 0 ; i<10 ;i ++){
await btn.waitClick;
console.log('按鈕被點擊了!!')
}
})();

這樣十次後就不監聽了!

如果想要拿到事件的參數 在後面添加個 e 就可以了

1
2
3
4
5
6
7
(async()=>{
const btn = getElement('button');
for (let i = 0 ; i<10 ;i ++){
const e =await btn.waitClick;
console.log('按鈕被點擊了!!', e)
}
})();
你的支持是我活力的來源 :)