【问题标题】:How to handle "single click" and "double click" on the same html DOM element using typescript:Angular 2 or 4?如何使用 typescript:Angular 2 或 4 在同一个 html DOM 元素上处理“单击”和“双击”?
【发布时间】:2021-12-18 05:14:58
【问题描述】:

我的问题是,当我执行“双击”时,用于这两个事件的方法都会被触发

例如,我需要在触发特定事件时执行特定功能。

<a (click)="method1()" (dblclick)="method2()">

当我执行“双击”时,method1()method2() 都会被触发。

【问题讨论】:

标签: angular


【解决方案1】:

您可以使用超时和布尔标志来解决此问题。 考虑以下几点:

DOM 需要几毫秒来识别双击。

但它肯定能识别双击但第一次点击也能识别。

所以逻辑是这样的。

isSingleClick: Boolean = true;     

method1CallForClick(){
   this.isSingleClick = true;
        setTimeout(()=>{
            if(this.isSingleClick){
                 doTheStuffHere();
            }
         },250)
}
method2CallForDblClick(){
         this.isSingleClick = false;
         doTheStuffDblClickHere();
}

在元素的点击事件中调用方法一,在元素的点击事件中调用方法二。

【讨论】:

  • 这不会将isSingle 重置为真。最重要的是,两次单击事件被触发,因为双击也是两次单击。如果您在某处重置标志,则必须忽略第二个。
【解决方案2】:

您可以使用纯 JavaScript 回调 dblclick,或者在 angular 2 中使用:(dblclick)="doubleClickFunction()"

如果您在同一个元素上同时具有 (click) 和 (dblclick),则应在单击事件回调中添加超时,以避免在用户双击时调用它。

尝试类似:

.html:

<button (click)="singleClick()" (dblclick)="doubleClick()">click</button>

.js:

singleClick(): void{
    this.timer = 0;
    this.preventSimpleClick = false;
    let delay = 200;

    this.timer = setTimeout(() => {
      if(!this.preventSimpleClick){
        ...
      }
    }, delay);

  }

  doubleClick(): void{
    this.preventSimpleClick = true;
    clearTimeout(this.timer);
    ...
  }

【讨论】:

  • 这可以在 angular/Typescript 本身而不是 javascript 帮助中完成
  • 这个函数是组件的一部分
【解决方案3】:
clickCount = 0;
click() {
    this.clickCount++;
    setTimeout(() => {
        if (this.clickCount === 1) {
             // single
        } else if (this.clickCount === 2) {
            // double
        }
        this.clickCount = 0;
    }, 250)
}

【讨论】:

  • 您好,欢迎来到 stackoverflow,感谢您的回答。虽然这段代码可能会回答这个问题,但您是否可以考虑添加一些解释来说明您解决了什么问题,以及您是如何解决的?这将有助于未来的读者更好地理解您的答案并从中学习。
【解决方案4】:

这样更干净

我找到了 rxjs 的解决方案,试试这个

我的模板有

<div #divElement></div>

我在我的 component.ts 中有

  @ViewChild('divElement') el: ElementRef;

  ngAfterViewInit(): void {
    this.handleClickAndDoubleClick();
  }

  handleClickAndDoubleClick(){
    const el = this.el.nativeElement;
    const clickEvent = fromEvent<MouseEvent>(el, 'click');
    const dblClickEvent = fromEvent<MouseEvent>(el, 'dblclick');
    const eventsMerged = merge(clickEvent, dblClickEvent).pipe(debounceTime(300));
    eventsMerged.subscribe(
      (event) => {
        if(event.type === 'click'){
          //click
        }

        //dblclick
      }
    );

【讨论】:

  • 这应该是公认的答案。谢谢阿曼多。
【解决方案5】:

我还没有找到在 Angular(或 Javascript)中本地执行此操作的方法,但这里有一种通用方法可以使其适用于所有点击处理程序。

虽然在概念上与其他答案相似,但其他答案不能很好地处理重复点击,并且不会在正确的时间维护(并重置!)他们的标志。

这里的想法是维护一个标志,用于在共享上下文(相同元素)的单击处理程序和 dblclick 处理程序之间做出决定:

  1. 点击处理程序会等待一小段时间以排除双击
  2. 双击处理程序在该等待期内阻止单击处理程序
  3. 因为双击意味着两个单单,在双击时,再次等待一小段时间再切换回标志,以确保第二个单击也被阻止

这是您在示例中使用它的方式:

    <a (click)="method1()" (dblclick)="method2()"></a>

在你的组件内部:

    const clickContext = new ClickContext();

    class YourComponent {
       method1 = clickContext.clickHandler(() => { /* your method1  */ });
       method2 = clickContext.dblClickHandler(() => { /* your method2 */ });
    }

对于需要 click 和 dblClick 处理程序的每个上下文(元素),您需要创建一个单独的上下文。幸运的是,这是一个罕见的要求。

现在,通过使用我将在下面介绍的魔法 util 类,可以正确处理单击和双击。

这是执行标志监视和处理程序执行的类:

    export class ClickContext {
        private isSingleClick = true;

        clickHandler(handler: ClickHandler): ClickHandler {
            return function() {
                const args = arguments;
                this.isSingleClick = true;
                setTimeout(() => {
                    this.isSingleClick && handler.apply(undefined, args);
                }, 250);
            }
        }

        dblClickHandler(handler: ClickHandler): ClickHandler {
            return function() {
                const args = arguments;
                this.isSingleClick = false;
                setTimeout(() => {
                    this.isSingleClick = true;
                }, 250);
                handler.apply(undefined, args);
            }
        }
    }

    type ClickHandler = (...any) => void;

请注意,要注意确保将原始参数传递给目标事件处理程序方法 1 和方法 2。

【讨论】:

    【解决方案6】:

    我刚刚为此创建了简单的解决方案。这里是模板:

    <button (click)="myFunction()">Click me!</button>
    

    这里是脚本:

    justClicked = false;
    doubleClicked = false;
    
    myFunction() {
      if (this.justClicked === true) {
        this.doubleClicked = true;
        this.doubleClick();
      } else {
        this.justClicked = true;
        setTimeout(() => {
          this.justClicked = false;
          if (this.doubleClicked === false) {
            this.singleClick();
          }
          this.doubleClicked = false;
        }, 500);
      }
    }
    
    singleClick() {
      console.log('single');
    }
    
    doubleClick() {
      console.log('double');
    }
    

    【讨论】:

      【解决方案7】:
      It needs simple work-round to block single click then do double click.  Check this example 
      

      @Component({
        selector: 'my-app',
        template: `
          <div>
            <h2>{{title}}</h2>
              <button (click)="singleClick($event)" (dblclick)="doubleClick( $event)"> Click </button>
          </div>
        `,
      })
      export class App {
        title:string;
        preventSingleClick = false;
        timer: any;
        delay: Number;
        constructor() {
          this.title = `Angular (click) and (dblclick)`
        
        }
        
        singleClick(event) {
          this.preventSingleClick = false;
           const delay = 200;
            this.timer = setTimeout(() => {
              if (!this.preventSingleClick) {
                 alert('Single Click Event');
              }
            }, delay);
        }
        
        doubleClick () {
          this.preventSingleClick = true;
          clearTimeout(this.timer);
          alert('Double Click Event')
        }
      }

      [plunker][1]

        [1]: http://plnkr.co/edit/pbsB0zYQCEY4xrPKngrF?p=preview
      

      【讨论】:

        【解决方案8】:

        更新(Angular 13):我已经在 Angular 中创建了一个指令来处理单击和双击事件,希望这有助于它只是一种解决方法。它的工作原理在代码 cmets 上。 (在评论部分开放以进行改进)?

        @Directive({
          // eslint-disable-next-line @angular-eslint/directive-selector
          selector: '[doubleTap]',
        })
        export class DBLTapGestureDirective implements OnInit {
          el: HTMLElement;
          private lastOnStart: number = 0;
          //double click threshold fires a double click event if there are 2 click within 230ms
          private DOUBLE_CLICK_THRESHOLD: number = 230;
          //single click threshold fires a single click after 250ms
          // during this time is listening for another click to si if it is a double click
          private SINGLE_CLICK_THRESHOLD: number = 250;
          private isSingleClick: boolean = true;
        
          //Emit Single and double click event
          @Output() doubleTap = new EventEmitter<void>();
          @Output() singleTap = new EventEmitter<void>();
        
          constructor(el: ElementRef, private gestureCtrl: GestureController) {
            this.el = el.nativeElement;
          }
        
          ngOnInit(): void {
            //Create gesture
            const gesture = this.gestureCtrl.create({
              el: this.el,
              threshold: 0,
              gestureName: 'my-gesture',
              onStart: () => {
                //Start Listening
                this.onStart();
              },
            });
            gesture.enable();
          }
        
          private onStart(): void {
            //get current time
            let now = Date.now();
            //check if time is within the double click threshold if not go to single click
            if (Math.abs(now - this.lastOnStart) <= this.DOUBLE_CLICK_THRESHOLD) {
              //emits double click event
              this.doubleTap.emit();
              //set single click false in order to not fire the single click event
              this.isSingleClick = false;
              //reset the last click timer
              this.lastOnStart = 0;
              //after the double click finished reset the single click to true to start listening again          for single click
              setTimeout(() => {
                this.isSingleClick = true;
              }, this.DOUBLE_CLICK_THRESHOLD);
            } else {
              //set a timeout before sending single click event to wait if there is a double click
              setTimeout(() => {
                if (this.isSingleClick) {
                  //fire single click event
                  this.singleTap.emit();
                  //set single click back to true
                  this.isSingleClick = true;
                }
              }, this.SINGLE_CLICK_THRESHOLD);
              //set last click time to now to make the compare in the next click
              this.lastOnStart = now;
            }
          }
        }
         <div
              (doubleTap)="like()"
              (singleTap)="openImage()"
              class="message-container">Image</div>

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
        【解决方案9】:

        这是我在角度中双击的方式

        clickme()
        {
          this.count++;
          if(count%2==0)
          { this.doubleclickme(); }
        }
        this.count is a global variable .
        this.doubleclickme() is a an other function in the component .
        

        【讨论】:

          【解决方案10】:
          itemClicked(event: MouseEvent) {
              if (event.type == 'click') {
                 
              }
            }
          
            itemdblClicked(event: MouseEvent) { 
              if (event.type.toLowerCase() == 'dblclick') {
                
              }
            }
          

          【讨论】:

          • 欢迎来到 Stack Overflow!虽然您的回答可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。您可以编辑您的答案以添加解释并指出适用的限制和假设。 - From Review
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-12-15
          • 1970-01-01
          • 2017-09-09
          • 1970-01-01
          • 2018-07-25
          • 1970-01-01
          • 2017-05-09
          相关资源
          最近更新 更多