Search code examples
javascripthtmlangulartouch

"Paint" table cells using swipe movements


I have a series of cells in an angular component:

<table>
  <thead role="rowgroup">
    <tr><th>M</th><th>T</th><th>O</th><th>T</th><th>F</th><th>L</th><th>S</th></tr>
  </thead>
  <tbody #body appSwipePaint>
    <tr *ngFor="let week of weeks" role="rowgroup">
      <td *ngFor="let day of week">
        <ng-container *ngIf="!!day.fromMs">
          <div (click)="book(day)" class="day">
            {{ day.fromMs | date: 'dd' }}
          </div>
        </ng-container>
      </td>
    </tr>
  </tbody>
</table>

My component looks like this:

Calendar cells

I want my users to be able to touch a date and swipe over to mark multiple dates. I've created a directive I'm attaching to my tbody to do this:

@Directive({selector: '[appSwipePaint]'})
export class SwipePaintDirective {
  private isPainting = false;
  private days: HTMLElement[] = [];

  constructor(private el: ElementRef) {
    this.el.nativeElement.addEventListener('pointerdown', (evt: PointerEvent) => this.touchStart(evt));
    this.el.nativeElement.addEventListener('pointermove', (evt: PointerEvent) => this.touchMove(evt));
    this.el.nativeElement.addEventListener('pointerup', (evt: PointerEvent) => this.touchEnd(evt));
  }

  touchStart($event: PointerEvent) {
    this.isPainting = true;
  }

  touchMove($event: PointerEvent) {
    const el = document.elementFromPoint($event.x, $event.y) as HTMLElement;
    if (this.isPainting && !this.days.includes(el)) {
      this.days.push(el);
      el.click();
    }
  }

  touchEnd($event: PointerEvent) {
    this.isPainting = false;
    this.days = [];
  }
}

This works fine on desktop. Browsers will detect that the PointerEvent enters each cell I'm pointing at as long as the mousebutton is pressed.

But when I test the page using a mobile device, or use simulated touch events in chrome dev-tools, the PointerEvents will only detect a touchstart and a touchmove within the cell which reported the touchstart. When I move the touch point outside the cell, it will no longer detect.

Why is that? What am I doing wrong?

Stackblitz example of my code


Solution

  • The solution was simple, but not at all intuitive. It was mentioned here: `pointermove` event not working with touch. Why not?

    Add touch-action: none; to the container element, which in this case appears to be tbody td.

    Revised stackblitz solution.