
## 前情提要
最近在製作**多選下拉**的元件,**多選下拉(multi-select)** 畫面的操作流程爲:
- **step1.** 點擊下拉選單展開多選選項窗
- **step2.** 勾選要選擇的選項
- **step3.** 關閉多選選項窗
其中第三步驟(step3)的關閉時機,希望是在「點擊非元件本身」的時候發生,目前有嘗試過:
==事件方面==
- **focusout**
在自製的元件最外層加上 **focusout**,讓點到任何地方都會馬上操作完並關閉。
> 問題:因爲會馬上關閉,所以如果是需要長時間操作的元件,會需要一直開開關關使用者體驗非常不佳
- **specific HTML**
在特定的 HTML 標籤加上觸發事件,並執行特定的關閉程式。
> 問題:難維護且不好判斷,剛開始元件需要判斷的地方不多的時候還挺好用的,但開始擴充功能後會讓判斷異常且會少改到要調整的地方
==畫面方面==
- **加上關閉按鈕**
在元件上提供關閉按鈕,讓使用者自行決定什麼時候要關閉
> 問題:當使用者不關閉的時候,元件就會一直顯示在畫面上,當元件疊加的時候使用者體驗非常不好,且不會自動關閉感覺非常不智慧
經過以上幾種的實驗以及時間的沉澱,這次我決定使用 `mousedown` 事件來處理判斷自動關閉這功能。
## 實做筆記
- 區別點擊的區域,若在非元件本身點擊就執行關閉。
==TS== **multi-select.component.ts**
```typescript=
import { CommonModule } from '@angular/common';
import { Component, ElementRef, EventEmitter, HostListener, Input, Output, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Subject, distinctUntilChanged, tap } from 'rxjs';
@Component({
selector: 'dm-multi-select',
standalone: true,
imports: [
CommonModule,
FormsModule,
],
templateUrl: './multi-select.component.html',
styleUrl: './multi-select.component.scss'
})
export class MultiSelectComponent {
//inject
#eleRef = inject(ElementRef);
//focus #非必要拆出 subject
#isFocus$: Subject<boolean> = new Subject<boolean>();
#isFocus: boolean = false;
//eventListener
@HostListener('window:mousedown', ['$event']) onMouseDown() {
//全域的點擊事件偵測,如果是點在自製區塊的話 #isFocus 會爲 true
if (this.#isFocus) return;
//點在非自製區塊就把元件關掉
/*this.showSelectList(false);*/
}
ngOnInit():void {
this.#eleFocus();
}
#eleFocus() {
const ele: HTMLElement = this.#eleRef.nativeElement;
if (!ele) return;
//點擊事件在自製元件內表示還在操作中,標示爲 focus
//#isFocus$ 爲本人測試寫法不一定要參考,可以單純用 #isFocus
ele.addEventListerner('mousedown', event => {
this.#isFocus$.next(true);
setTimeout(() => {
this.#isFocus$.next(false);
});
});
//#isFocus$ 爲本人測試寫法不一定要參考,可以單純用 #isFocus
this.#isFocus$.pipe(
distinctUntilChanged(),
tap((res) => this.#isFocus = res),
).subscribe();
}
}
```
## 問題集
### 🤔 window.document.addEventListener 截段所有事件?
在實現全域 `mousedown` 事件偵測得時候,原本是這樣寫:
```typescript!
#eleFocus() {
...
window.document.addEventListener('mousedown', event => {
if (this.#isFocus) return;
this.showSelectList(false);
});
...
}
```
當時在測試單獨的時候其實沒什麼問題,但跟選單元件整合測試時,就發現選單的切換頁功能失效了,但這部分還沒測試也還沒找問題是什麼
因此我就把這段拿掉改用`@HostListener` 來實現。
```typescript
@HostListener('window:mousedown', ['$event']) onMouseDown() {
if (this.#isFocus) return;
this.showSelectList(false);
}
```