使用RxJ进行单击和双击的单独操作

时间:2018-09-05 20:45:54

标签: javascript angular rxjs

我在使用RxJ时遇到问题。

单击一次时,我需要在console.log中记录一条消息,而单击按钮两次时,我需要在日志中记录一条消息。问题是:

    如果我第一次单击,一开始是
  • -没有任何反应-错误 (我应该看到第一条消息“单击”)

  • 然后,如果我单击按钮,然后(一秒钟后)再次单击,则第二次单击我会看到两条消息-错误(每次操作我应该只看到一条消息)

    < / li>
  • 当我单击按钮时,请等待一秒钟,然后再次单击,再等待一秒钟,然后再次单击,然后单击最后一次,我将在console.log中看到三则消息-错误(每次操作我只能看到一条消息)
  • 在那之后,如果我单击两次(双击),我将看到5条双击消息,以及1条单击消息-错误(我应该只看到一条消息“双击”)

我想要的是:

  • 如果我单击一次,则只需要看到一条消息(“一次单击”)
  • 如果我单击两次(双击),仍然只需要看到一条消息(“双击”)
  • 如果我单击两次以上-没有任何反应

有什么帮助吗?

check hear for examples

4 个答案:

答案 0 :(得分:2)

一个非常简单的答案(不使用任何可观察变量)是使用setTimeout()并检查每次点击是否设置了超时,如果这样,则表示这是给定时间范围内的第二次点击(两次点击),如果不是,则是第一次点击。如果超时到期,则您只需单击一下即可,就像这样:

Updated StackBlitz

// have a timer that will persist between function calls
private clickTimeout = null;
public test(event): void {
  // if timeout exists, we know it's a second click within the timeout duration
  // AKA double click
  if (this.clickTimeout) {
    // first, clear the timeout to stop it from completing
    clearTimeout(this.clickTimeout);
    // set to null to reset
    this.clickTimeout = null;
    // do whatever you want on double click
    console.log("double!");
  } else {
  // if timeout doesn't exist, we know it's first click
    this.clickTimeout = setTimeout(() => {
      // if timeout expires, we know second click was not handled within time window
      // so we can treat it as a single click
      // first, reset the timeout
      this.clickTimeout = null;
      // do whatever you want on single click
      console.log("one click");
    }, 400);
  }
}

编辑

我错过了忽略2次以上点击的部分。它并不需要更多的工作,但是为了使代码可以重用,我做了更多的工作,所以看起来还很多。无论如何,要忽略3次以上的点击,它看起来将如下所示:

// count the clicks
private clicks = 0;
private clickTimeout = null;
public test(event): void {
  this.clicks++;
  if (this.clickTimeout) {
    // if is double click, set the timeout to handle double click
    if (this.clicks <= 2) {
      this.setClickTimeout(this.handleDoubleClick);
    } else {
    // otherwise, we are at 3+ clicks, use an empty callback to essentially do a "no op" when completed
      this.setClickTimeout(() => {});
    }
  } else {
    // if timeout doesn't exist, we know it's first click - treat as single click until further notice
    this.setClickTimeout(this.handleSingleClick);
  }
}
// sets the click timeout and takes a callback for what operations you want to complete when the
// click timeout completes
public setClickTimeout(cb) {
  // clear any existing timeout
  clearTimeout(this.clickTimeout);
  this.clickTimeout = setTimeout(() => {
    this.clickTimeout = null;
    this.clicks = 0;
    cb();
  }, 400);
}
public handleSingleClick() {
  console.log("one click");
}
public handleDoubleClick() {
  console.log("double!");
}

答案 1 :(得分:1)

这是您想要的东西,或者非常接近它:

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})

export class AppComponent  {

  @ViewChild('mybutton') button: ElementRef;

  ngAfterViewInit() {
    fromEvent(this.button.nativeElement, 'dblclick')
    .subscribe(e => console.log('double click'));

    fromEvent(this.button.nativeElement, 'click')
    .subscribe((e: MouseEvent) => {
      if (e.detail === 1) console.log('one click') // could use a filter inside a pipe instead of using an if statement
      // if (e.detail === 2) console.log('double click') // an alternative for handling double clicks
    });
  }
}

和HTML:

<button #mybutton>Test</button>

此解决方案使用event.detail,并且宽松地基于此有用的帖子-Prevent click event from firing when dblclick event fires-该帖子不仅讨论event.detail,还着眼于所涉及的计时问题。

您的代码中的一个大问题是,您正在多次调用的函数内预订事件,这意味着每次单击按钮时,您都会创建另一个订阅。使用ngAfterViewInit(在生命周期中仅被调用一次)可以避免此问题(并确保DOM被加载)。

这是给你的惊喜:

https://stackblitz.com/edit/angular-t6cvjj

请原谅我草率的类型声明!

这满足您的要求,如下:

  • 如果单击一次,则只需要看到一条消息(“单击”)-通过
  • 如果我单击两次(双击),仍然只需要看到一条消息(“双击”)-失败,您会在第一次单击时看到“一击”,而在第二次单击时看到“双击”
  • 如果我单击两次以上-没有任何反应-通过

由于在SO帖子中讨论的时间问题,如何解决这个问题是一个优先事项,因此我决定不解决它。而且,您不付钱给我;)但是,此答案应该为您提供了一个完全解决之道的好方法。

PS在上面的SO帖子上有一条评论表明event.detail可能不适用于IE11

答案 2 :(得分:1)

@Siddharth Ajmeras的答案显示了如何通过事件进行此操作。我不知道存在dblclick事件。你懂得越多。如果您仍然对如何使用rxjs进行操作感兴趣,请参考以下示例。

// How fast does the user has to click
// so that it counts as double click
const doubleClickDuration = 100;

// Create a stream out of the mouse click event.
const leftClick$ = fromEvent(window, 'click')
// We are only interested in left clicks, so we filter the result down
  .pipe(filter((event: any) => event.button === 0));

// We have two things to consider in order to detect single or
// or double clicks.

// 1. We debounce the event. The event will only be forwared 
// once enough time has passed to be sure we only have a single click
const debounce$ = leftClick$
  .pipe(debounceTime(doubleClickDuration));

// 2. We also want to abort once two clicks have come in.
const clickLimit$ = leftClick$
  .pipe(
    bufferCount(2),
  );


// Now we combine those two. The gate will emit once we have 
// either waited enough to be sure its a single click or
// two clicks have passed throug
const bufferGate$ = race(debounce$, clickLimit$)
  .pipe(
    // We are only interested in the first event. After that
    // we want to restart.
    first(),
    repeat(),
  );

// Now we can buffer the original click stream until our
// buffer gate triggers.
leftClick$
  .pipe(
    buffer(bufferGate$),
    // Here we map the buffered events into the length of the buffer
    // If the user clicked once, the buffer is 1. If he clicked twice it is 2
    map(clicks => clicks.length),
  ).subscribe(clicks => console.log('CLicks', clicks));

答案 3 :(得分:0)

您可以创建一个监听nologging的指令,然后在该指令中执行有需要的操作。

dblclick

模板:

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: '[appDoubleClick]'
})
export class DoubleClickDirective {
  constructor() { }

  @HostListener('dblclick') onDoubleClicked() {
    console.log('onDoubleClicked ran!');
  }

}

UPDATED STACKBLITZ

不确定在iOS中是否可以使用。但是请尝试一下。