如何使用 typescript:Angular 2 或 4 处理同一个 html DOM 元素上的"single click"和"double click"?



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

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

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

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

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

DOM 需要几毫秒才能识别双击。

但可以肯定的是,它可以识别双击,但第一次单击也被识别。

所以逻辑是这样的。

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

在元素的单击事件中调用方法 1,在元素的单击事件中调用方法 2。

你可以将纯JavaScript回调dblclick,或者像你在angular 2中用到的那样:(dblclick)="doubleClickFunction()"

如果在同一元素上同时具有(单击)和(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);
...
}
clickCount = 0;
click() {
this.clickCount++;
setTimeout(() => {
if (this.clickCount === 1) {
// single
} else if (this.clickCount === 2) {
// double
}
this.clickCount = 0;
}, 250)
}

这更干净

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

我的模板有

<div #divElement></div>

我在我的组件中都有

@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
}
);

我还没有找到在 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 */ });
}

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

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

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

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 和 method2。

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')
}
}

[普伦克][1]

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

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

@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>

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

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 .

与 https://stackoverflow.com/a/60877770/9970629 中的阿曼多类似的方法 - 但就我而言,我不太想使用ViewChild.

此方法使用 ReplaySubject。本质上,我为单击事件定义了一个接口,并为所有收到的单击事件和双击事件定义了重播主题。然后,我将真正的单击(单击而不双击)定义为两者与去抖动的合并,以及仅发出单击的过滤器:

interface ClickRowEvent<T extends any = any> {
clickType: "single" | "double";
data: T;
}
@Component({
selector: "foo",
template: `
<div *ngFor let thing of things>
<div 
(click)="handleSingleClick(thing) 
(dblclick)="handleDoubleClick(thing)"
>
{{thing}}
</div>
</div>
`
})
export class MyComponent {
things = ["foo", "bar", "baz"];
private _singleClickEvents: ReplaySubject<ClickRowEvent>;
private singleClickEvents$: Observable<ClickRowEvent>;
private _doubleClickEvents: ReplaySubject<ClickRowEvent>;
private doubleClickEvents$: ReplaySubject<ClickRowEvent>;
constructor() {
this._singleClickEvents = new ReplaySubject<ClickRowEvent>();
this._doubleClickEvents = new ReplaySubject<ClickRowEvent>();

this.doubleClickEvents$ = this._doubleClickEvents.asObservable();
const allClicks$ = this._singleClickEvents.asObservable();
this.singleClickEvents$ = merge(allClicks$, this.doubleClickEvents$).pipe(
debounceTime(250),
filter((e) => e.clickType === "single")
);
}
handleSingleClick(data: T): void {
this._singleClickEvents.next({ clickType: "single", data });
}
handleDoubleClick(data: T): void {
this._doubleClickEvents.next({ clickType: "double", data });
}
}

现在,您可以订阅所有singleClickEvents$doubleClickEvents$,而无需额外的状态管理。当然,如果您不想要额外的clickType属性,您也可以映射回数据/事件。

您可以使用事件参数

网页模板:

<div (click)="showModal($event)"></div>

元件

showModal(e) {
if(e.detail == 2) {
//blabla...
}
}

我只是偶然发现了这个,但我不喜欢 setTimeout 的答案,因为单击的反应时间也会延迟。我想要对单击的即时反应,但要防止双击时发生两次单击事件。

我的情况很特殊,我也想处理单击。所以这可能并没有真正回答这个问题,因为我同时做 - 单击和双击 - 对我来说唯一的事情是,当双击触发时,我不执行第二个单击事件。

我的解决方案很简单,我只是测量两次点击之间花费的时间,如果它超过 500 毫秒(dblclick 的默认延迟),那么我将执行单击代码。 如果没有,则 dblclick 将触发,而第二次单击不会。

clicked = 0;
clickHandler() {
if (Date.now() - this.clicked > 500) { // change threshold as you need
this.clicked = Date.now();
// click logic here
}
}
dblClickHandler() {
// dblclick logic here, no extra code needed
}

itemClicked(event: MouseEvent) {
if (event.type == 'click') {

}
}
itemdblClicked(event: MouseEvent) { 
if (event.type.toLowerCase() == 'dblclick') {

}
}

最新更新