我的问题是,当我执行“双击”时,两个事件所使用的方法都会被触发。
例如,我需要在特定事件触发时执行特定功能。
<a (click)="method1()" (dblclick)="method2()">
当我执行 "双击" 时,method1()
和 method2()
都会被触发。
我的问题是,当我执行“双击”时,两个事件所使用的方法都会被触发。
例如,我需要在特定事件触发时执行特定功能。
<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();
}
在元素的点击事件中调用方法一,在另一个元素的点击事件中调用方法二。
您可以直接使用纯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);
...
}
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>
我在我的组件.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
}
);
<a (click)="method1()" (dblclick)="method2()"></a>
在你的组件内部:
const clickContext = new ClickContext();
class YourComponent {
method1 = clickContext.clickHandler(() => { /* your method1 */ });
method2 = clickContext.dblClickHandler(() => { /* your method2 */ });
}
对于每个需要同时具有单击和双击处理程序的上下文(元素),您需要创建一个单独的上下文。幸运的是,这是一个罕见的要求。
现在,通过使用我将在下面介绍的神奇实用类,可以正确处理单击和双击。
这是监视标志并执行处理程序的类:
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;
我刚刚为此创建了一个简单的解决方案。以下是模板:
最初的回答:
<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');
}
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
更新(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>
与https://dev59.com/plYM5IYBdhLWcg3w1S3d#60877770中的Armando类似的方法 - 但在我的情况下,我并不想使用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
属性,您也可以将其映射回数据/事件。
我刚刚偶然发现这个问题,但是我不喜欢使用setTimeout的答案,因为单击的反应时间也会被延迟。我想要立即响应单击事件,但是防止双击时触发两次单击事件。
我的情况比较特殊,我希望同时处理单击和双击事件。所以这可能并没有真正回答问题,因为我同时处理单击和双击事件,唯一的区别是当双击事件触发时,我不再执行第二次单击事件。
我的解决方案很简单,我只需要测量两次点击之间的时间,如果超过500毫秒(默认的双击延迟时间),那么我将执行单击代码。否则,双击事件将触发,第二次单击事件将不会被执行。
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
}