16 April 2019

Angular 2+ Directive for making an element un-clickable until the next tick

This directive will prevent clickable elements from being clicked again before the next 'tick'. I hadn't seen this particular solution in the wild, and it seems to be the most effective so it's worth sharing.

If you want a DOM element to be covered up or otherwise disabled after being clicked, it may still be visible for a split second before the next round of change detection updates the DOM to do so.

A user with a fast clicky finger or tendency to double click everything even when it isn't necessary could get an extra click in, causing an an unnecssary command. Especially frustrating if it's a save button and two items are created instead of one.

stopPropogation() and preventDefault() functions on the click event were not effective in preventing all actions - however disabling pointer events via the style sheet was very effective in preventing errant clicks from registering. The clicked element is made un-clickable until the next tick via a 0 second setTimeout. Check out the full solution below.

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

@Directive({
    selector: '[click],[type="submit"]',
})
export class ClickWaiterDirective {

    constructor() { }

    @HostListener("click", ["$event"])
    public onClick(event: MouseEvent): void {
        //Disable the target immediately after click
        let targetElement = <HTMLElement>event.target;// Must cast as HTMLElement or style cannot be modified
        targetElement.style.pointerEvents = 'none';// When css attribute pointer-events is set to 'none', the element becomes unclickable :) 
        //Only allow another click next 'tick' using setTimeout
        setTimeout(()=>{
            targetElement.style.pointerEvents = 'auto';// When css attribute pointer-events is set to 'auto', the element becomes clickable again
        }, 0);
    }

}

To use this, simply copy and paste the code into a new directive and reference it in your app.module.ts file. It will be applied to any form submit button or input, as well as any element with a (click) action