You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中点击事件需二次触发的问题排查

问题原因与修复方案

核心问题

点击建议项时,输入框的blur事件会先于click事件触发,导致priceFocused被设为false,建议框通过*ngIf被销毁,此时click事件因元素已不存在无法执行。第一次点击实际只是触发了输入框失焦、隐藏建议框,第二次点击时输入框重新获焦显示建议框,才能触发点击逻辑。


修复方案一:监听全局点击事件(推荐)

通过监听document点击,判断点击区域是否为输入框或建议框,避免误触发隐藏逻辑。

修改后的HTML代码

<div class="input-field width-100">
    <label>Price</label>
    <input autocomplete="off" type="number" class="w-100" value="0.00" name="price"
        (focus)="showPriceSuggestions()" 
        [(ngModel)]="currentInventory.price" (ngModelChange)="onFieldChange()">
    <!-- 添加模板引用变量 #priceSuggestions -->
    <div #priceSuggestions class="suggestions suggestions-wider" *ngIf="priceFocused">
        <ul>
            <ng-container *ngIf="currentInventory.card.tcgplayer.prices != null">
                <ng-container *ngFor="let price of formatPriceData()">
                    <li class="card-choice" *ngIf="price != null"
                        (click)="selectPrice(price[1])">
                        <div class="row">
                            <div class="col-8">
                                {{price[0]}}
                            </div>
                            <div class="col-4">
                                ${{price[1]}}
                            </div>
                        </div>
                    </li>
                </ng-container>
            </ng-container>
        </ul>
    </div>
</div>

修改后的TS代码

import { Component, ElementRef, Renderer2, ViewChild } from '@angular/core';

// 替换为你的组件类名
export class InventoryComponent {
    public priceFocused: boolean = false;
    @ViewChild('priceSuggestions') priceSuggestions!: ElementRef;
    private clickListener?: () => void;

    constructor(private el: ElementRef, private renderer: Renderer2) {}

    showPriceSuggestions() {
        this.priceFocused = true;
        // 仅在显示建议框时监听全局点击
        if (!this.clickListener) {
            this.clickListener = this.renderer.listen('document', 'click', (event) => {
                this.handleOutsideClick(event);
            });
        }
    }

    selectPrice(price: number) {
        this.currentInventory.price = price;
        this.priceFocused = false;
        // 移除全局监听
        this.cleanupClickListener();
    }

    private handleOutsideClick(event: MouseEvent) {
        const inputElement = this.el.nativeElement.querySelector('input[name="price"]');
        const suggestionsEl = this.priceSuggestions?.nativeElement;

        // 点击区域不是输入框也不是建议框时,隐藏建议
        if (!inputElement.contains(event.target) && (!suggestionsEl || !suggestionsEl.contains(event.target))) {
            this.priceFocused = false;
            this.cleanupClickListener();
        }
    }

    private cleanupClickListener() {
        if (this.clickListener) {
            this.clickListener();
            this.clickListener = undefined;
        }
    }

    // 移除原有的priceFieldBlur方法
}

修复方案二:利用mousedown事件顺序

mousedown事件触发时机早于blur,因此可以用mousedown替代click处理选择逻辑,避免元素被提前销毁。

修改后的HTML代码(仅修改li部分)

<li class="card-choice" *ngIf="price != null"
    (mousedown)="selectPrice(price[1])">
    <div class="row">
        <div class="col-8">
            {{price[0]}}
        </div>
        <div class="col-4">
            ${{price[1]}}
        </div>
    </div>
</li>

修改后的TS代码

public priceFocused: boolean = false;

showPriceSuggestions() {
    this.priceFocused = true;
}

selectPrice(price: number) {
    this.currentInventory.price = price;
    this.priceFocused = false;
}

priceFieldBlur() {
    this.priceFocused = false;
}

内容的提问来源于stack exchange,提问作者andresmonc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 01:45:57