359

What is the easiest way to stop mouse events propagation in Angular ?

Should I pass special $event object and call stopPropagation() myself or there is some other way.

For example in Meteor, I can simply return false from event handler.

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
Roman Kolesnikov
  • 11,777
  • 11
  • 44
  • 67

15 Answers15

356

If you want to be able to add this to any elements without having to copy/paste the same code over and over again, you can make a directive to do this. It is as simple as below:

import {Directive, HostListener} from "@angular/core";
    
@Directive({
    selector: "[click-stop-propagation]"
})
export class ClickStopPropagation
{
    @HostListener("click", ["$event"])
    public onClick(event: any): void
    {
        event.stopPropagation();
    }
}

Then just add it to the element you want it on:

<div click-stop-propagation>Stop Propagation</div>
HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
dnc253
  • 39,967
  • 41
  • 141
  • 157
304

The simplest is to call stop propagation on an event handler. $event works the same in Angular 2, and contains the ongoing event (by it a mouse click, mouse event, etc.):

(click)="onEvent($event)"

on the event handler, we can there stop the propagation:

onEvent(event) {
   event.stopPropagation();
}
Angular University
  • 42,341
  • 15
  • 74
  • 81
204

Calling stopPropagation on the event prevents propagation: 

(event)="doSomething($event); $event.stopPropagation()"

For preventDefault just return false

(event)="doSomething($event); false"

Event binding allows to execute multiple statements and expressions to be executed sequentially (separated by ; like in *.ts files.
The result of last expression will cause preventDefault to be called if falsy. So be cautious what the expression returns (even when there is only one)

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I haven't tried it myself but https://github.com/angular/angular/issues/4782 and https://github.com/angular/angular/pull/5892/files indicate it should work. They don't have the `;` at the end though. I'll change that. – Günter Zöchbauer Feb 08 '16 at 16:18
  • 1
    They are talking about preventing default action, not stepping event through parent elements what is stopPropagation is about. – Roman Kolesnikov Feb 08 '16 at 16:26
  • Oh, that's my bad. Sorry. – Günter Zöchbauer Feb 08 '16 at 16:28
  • 2
    return `false` <3 – Pawel Gorczynski Jul 23 '18 at 11:11
  • Amazing how wrong answers get auto upvotes. The code simply does not work. – Shadow The GPT Wizard Jun 25 '19 at 06:14
  • 2
    @ShadowWizard That does not necessarily mean the answer is wrong. The error could also be on your side, or that the answer could have been correct for some time and then something has changed. – Günter Zöchbauer Jun 25 '19 at 06:58
  • No error, the page simply reloaded after clicking a link with Angular (click) event. Turns out I had to use event.preventDefault(), and not stopPropagation(). – Shadow The GPT Wizard Jun 25 '19 at 07:14
  • 2
    @ShadowWizard there are newer answers even with more upvotes. Either try these or create a new question with full information about your use case (stackblitz reproduction, ....) instead of accusing hundred random people of upvoting for no reason. – Günter Zöchbauer Jun 25 '19 at 07:28
  • also use `stopImmediatePropagation` for preventing other listener to be called – Aleris Oct 19 '19 at 09:16
  • 1
    This worked fantastically. I needed to show a tooltip next to a dropdown which, when expanded, would block the tooltip: `` – ShortM Jul 10 '20 at 07:56
  • 2
    With `strict` template checking you'll need to do something like `(selectionChange)="selectionChange($event); $any($event).stopPropagation()"`, or move it to your handler. – Simon_Weaver Jun 18 '21 at 18:23
55

Adding to the answer from @AndroidUniversity. In a single line you can write it like so:

<component (click)="$event.stopPropagation()"></component>
dinigo
  • 6,872
  • 4
  • 37
  • 48
16

This worked for me:

mycomponent.component.ts:

action(event): void {
  event.stopPropagation();
}

mycomponent.component.html:

<button mat-icon-button (click)="action($event);false">Click me !<button/>
Victor Zamanian
  • 3,100
  • 24
  • 31
Brahim JDIDOU
  • 163
  • 1
  • 6
14

I had to stopPropagation and preventDefault in order to prevent a button expanding an accordion item that it sat above.

So...

@Component({
  template: `
    <button (click)="doSomething($event); false">Test</button>
  `
})
export class MyComponent {
  doSomething(e) {
    e.stopPropagation();
    // do other stuff...
  }
}
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
BrandonReid
  • 1,264
  • 1
  • 13
  • 17
10

If you're in a method bound to an event, simply return false:

@Component({
  (...)
  template: `
    <a href="/test.html" (click)="doSomething()">Test</a>
  `
})
export class MyComp {
  doSomething() {
    (...)
    return false;
  }
}
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 1
    It __does__ work for click events. At least in the latest version of Angular2. – Jon Oct 25 '16 at 02:26
  • 7
    Returning `false` calls `preventDefault` not `stopPropagation`. – Günter Zöchbauer Jan 04 '17 at 17:07
  • Calling return false does stop propagation in the DOM. Check your facts @GünterZöchbauer It's mentioned in the ng-book. – moeabdol Sep 05 '17 at 12:24
  • 4
    @moeabdol a link could at least demonstrate what you claim, but actually `preventDefault` is called. https://github.com/angular/angular/blob/450a13dea03b5b3cd3b92c5a840a6536ec003f83/packages/core/src/metadata/directives.ts#L215, https://github.com/angular/angular/blob/6279e50d78554781111053e41cafcc8ca50ecc97/packages/core/test/view/element_spec.ts#L264 – Günter Zöchbauer Sep 05 '17 at 12:31
  • 1
    Correct! @GünterZöchbauer Thank you for clarifying this. I wish I could share a link. I followed Nate Murray's advice in his book "ng-book The Complete Book on Angular 4" of returning false; at the end of a function to stop event propagation in the DOM. He describes it exactly the way you mentioned. Sorry for the misunderstanding. – moeabdol Sep 05 '17 at 17:58
4

I just checked in an Angular 6 application, the event.stopPropagation() works on an event handler without even passing $event

(click)="doSomething()"  // does not require to pass $event


doSomething(){
   // write any code here

   event.stopPropagation();
}
Dipendu Paul
  • 2,685
  • 1
  • 23
  • 20
  • 2
    This is because you're accessing `window.event`. Technically this is deprecated https://developer.mozilla.org/en-US/docs/Web/API/Window/event. It should work, but could in future trip you up. – Simon_Weaver Jun 18 '21 at 17:59
3

Nothing worked for IE (Internet Explorer). My testers were able to break my modal by clicking off the popup window on buttons behind it. So, I listened for a click on my modal screen div and forced refocus on a popup button.

<div class="modal-backscreen" (click)="modalOutsideClick($event)">
</div>


modalOutsideClick(event: any) {
   event.preventDefault()
   // handle IE click-through modal bug
   event.stopPropagation()
   setTimeout(() => {
      this.renderer.invokeElementMethod(this.myModal.nativeElement, 'focus')
   }, 100)
} 
PatrickW
  • 357
  • 3
  • 4
3

I used

<... (click)="..;..; ..someOtherFunctions(mybesomevalue); $event.stopPropagation();" ...>...

in short just seperate other things/function calls with ';' and add $event.stopPropagation()

Ray Dragon
  • 84
  • 1
  • 3
1

Disable href link with JavaScript

<a href="#" onclick="return yes_js_login();">link</a>

yes_js_login = function() {
     // Your code here
     return false;
}

How it should also work in TypeScript with Angular (My Version: 4.1.2)

Template
<a class="list-group-item list-group-item-action" (click)="employeesService.selectEmployeeFromList($event); false" [routerLinkActive]="['active']" [routerLink]="['/employees', 1]">
    RouterLink
</a>
TypeScript
public selectEmployeeFromList(e) {

    e.stopPropagation();
    e.preventDefault();

    console.log("This onClick method should prevent routerLink from executing.");

    return false;
}

But it does not disable the executing of routerLink!

Javan R.
  • 2,011
  • 1
  • 19
  • 16
1

Adding false after function will stop event propagation

<a (click)="foo(); false">click with stop propagation</a>
Fabian Rios
  • 171
  • 1
  • 7
1

This solved my problem, from preventign that an event gets fired by a children:

doSmth(){
  // what ever
}
        <div (click)="doSmth()">
            <div (click)="$event.stopPropagation()">
                <my-component></my-component>
            </div>
        </div>
JulianRot
  • 11
  • 2
1

Try this directive

@Directive({
    selector: '[stopPropagation]'
})
export class StopPropagationDirective implements OnInit, OnDestroy {
    @Input()
    private stopPropagation: string | string[];

    get element(): HTMLElement {
        return this.elementRef.nativeElement;
    }

    get events(): string[] {
        if (typeof this.stopPropagation === 'string') {
            return [this.stopPropagation];
        }
        return this.stopPropagation;
    }

    constructor(
        private elementRef: ElementRef
    ) { }

    onEvent = (event: Event) => {
        event.stopPropagation();
    }

    ngOnInit() {
        for (const event of this.events) {
            this.element.addEventListener(event, this.onEvent);
        }
    }

    ngOnDestroy() {
        for (const event of this.events) {
            this.element.removeEventListener(event, this.onEvent);
        }
    }
}

Usage

<input 
    type="text" 
    stopPropagation="input" />

<input 
    type="text" 
    [stopPropagation]="['input', 'click']" />
j3ff
  • 5,719
  • 8
  • 38
  • 51
David Alsh
  • 6,747
  • 6
  • 34
  • 60
-1

Most of the solutions provided are for versions above Angular 11, For Angular 11 or Below, I found a workaround that can be used:

export class UiButtonComponent implements OnInit, OnDestroy {

  @Input() disabled = false;

  clickEmitter: Subject<any> = new Subject();

  constructor(private elementRef: ElementRef) { }

  ngOnInit(): void {
    this.elementRef.nativeElement.eventListeners()
      .map(listener => this.clickEmitter.pipe(filter(event => Boolean(event))).subscribe(event => listener(event)));
    this.elementRef.nativeElement.removeAllListeners();
    this.elementRef.nativeElement.addEventListener('click', (event) => {
      if (!this.disabled) {
        this.clickEmitter.next(event);
      }
    });
  }

  ngOnDestroy(): void {
    this.clickEmitter.complete();
  }
}

I basically take every listener to the current component and put it on an Observable, then, I register only one listener and manage there the actions.

The example above is an example of disabling the click event on a button given a boolean variable.

  • Looks like you are solving different problem. There are many answers that works for Angular2+. Also you touching nativeElement. You should avoid doing this in any angular version. – Roman Kolesnikov Aug 09 '21 at 13:30