如何正确处理混合设备的点击/触摸事件?

9

我正在尝试了解如何在处理触摸和点击事件时使用混合设备,但我找不到任何真正有效的解决方案(我没有混合设备,因此无法直接测试,但由于失败的尝试甚至不能在常规设备上工作,我假设它们在混合设备上也不能工作)。

问题在于,在混合设备上,您必须同时进行触摸和点击事件绑定,而不会触发函数两次。 因此,如果您查看我的失败尝试(2和3),您会发现我绑定了touchendclick,但似乎存在某种语法错误或其他错误,因为这导致事件实际上不会触发。

第一个解决方案很好用,但那只是在我使用事件触发类型中的其中一个时。

我到目前为止尝试的:

1 - 在触摸设备和点击设备上都有效:

_renderer.listenGlobal('document', 'ontouchstart' in window ? 'touchend' : 'click', (e) => {

  console.log('works');  
});

2 - 不支持触摸或点击设备:

_renderer.listenGlobal('document', 'touchend click', (e) => {

  console.log('works');

  e.stopPropagation(); 
});

3 - 不触发触摸或点击设备的事件:

_renderer.listenGlobal('document', 'touchend, click', (e) => {

  console.log('works');  

  e.stopPropagation();
});

如您所见,第一个示例涵盖了2/3的设备类型,而其他示例则涵盖了0个。

我该如何确保我的函数在每个设备上都能正常运行?

2个回答

9
你可以使用Subject和防抖动(debounce)技术,让事件在几毫秒内只触发一次,像这样:

你可以这么做:

import {Component, Renderer} from '@angular/core'
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
    </div>
  `,
})
export class App {
  name = 'Angular2';
  subject = new Subject();
  
  constructor(renderer: Renderer) {
    renderer.listenGlobal('document', 'touchend', (e) => {
      console.log('touchend');
      this.subject.next(e);
    });
    renderer.listenGlobal('document', 'click', (e) => {
      console.log('click');
      this.subject.next(e);
    });

    this.subject.debounceTime(100).subscribe(event => {
      console.log(event); //do stuff here
    })
  }
}

当您使用混合设备时,您将得到以下结果: enter image description here 两个事件被触发,但是在Observable中只会得到一个。
您可以在这个plunker中进行实验。

我很感兴趣,我会更加彻底地调查这个。 - Chrillewoodz

6

只需在组件上添加(tap)指令而不是(click)指令,并将hammerjs添加到您的index.html文件中。

Angular 2会为您完成所有工作。

示例:

<my-component (tap)="doSomething()"></my-component>

在您的index.html文件中添加以下内容:
<script src="hammer.min.js"></script>

为获得hammerjs,请执行以下操作:
npm install hammerjs --save

通过这样的方法,点击和触摸都可以正常工作。如果您想更好地控制触摸事件或者想在运行时绑定元素事件,请尝试以下代码。

_hammerEvents: HammerManager;

public bindTapEvent(element: ElementRef):void{
    this._hammerEvents = new Hammer(element.nativeElement);
    this._hammerEvents.on("tap", (event:any) => { /* do something */});
}

你能解释一下为什么我需要使用hammerjs吗? - Chrillewoodz
@Chrillewoodz Angular 2已经有一个指令(tap)专门用于这种情况,该指令响应触摸事件(ontouchstart,touchend),并且可以与单击事件一起使用。在幕后,Angular 2将检查是否加载了hammerjs。 - dlcardozo
你知道如何将一个tap绑定到文档吗?我收到一个错误,说它没有被实现。 - Chrillewoodz
谢谢@dlcardozo。它非常好用。我在桌面电脑、安卓手机和iOS 12的iPad上进行了测试。 - Nikhil
1
这应该是正确的答案。对于 Angular 来说非常棒,而且正是我所需要的缺失知识。 - ttugates

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接