![image](https://hackmd.io/_uploads/rkViO7Yap.png) ## 前情提要 最近在製作**多選下拉**的元件,**多選下拉(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); } ```