最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular 5 - How to trigger event when user clicks outside targeted elements - Stack Overflow

programmeradmin2浏览0评论

I need to be able to register when a user clicks outside targeted elements. To do this I have created a custom attribute directive shown below.


ClickOutsideTargets Directive

import { Directive, ElementRef, Input, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[clickOutsideTargets]',
})
export class ClickOutsideTargetsDirective {

    @Input()
    public targets : HTMLElement[] = [];


    @Output() 
    clickOutsideTargets = new EventEmitter();

    constructor(){

    }


    @HostListener('document:click', ['$event.target'])
    public onClick(targetElement){

     if(!targetElement){
        return;
    }

    for(var i = 0, len = this.targets.length; i < len; i++){
        if(this.targets[i].contains(targetElement)){
            return;
        }
    }

    this.clickOutsideTargets.emit(null);

  }

}

The 'targets' array specifies the elements where the user can click without triggering an event. Whenever the user clicks outside these elements an event is emitted.


I want to be able to pass an array of targets to the directive as an input, using the following syntax which is clean and convenient. Each target is given as a template variable (#variable).

<button #buttonRef class="btn btn-primary" (click) = "activate()" (clickOutsideTargets) = "deactivate()" [targets] = "[buttonRef, containerRef]"> </button>



Issues with this approach

However, this doesn't work when-

  • One of the target elements exists in the template of a child ponent. Unless there is a way to access a child's template variables from the parent ponent's template.

  • One of the target elements is created dynamically, for example through an NgFor.


Is there a way to solve these issues? Or ideally, is there a better solution/strategy that I'm not considering.


(One really messsy solution to get the functionality is by creating a new template variable to represent the directive (using exportAs within the directive's decorator), accessing the directive within the parent ponent using ViewChild, accessing the target elements using ViewChild/ViewChildren and manually setting the directive's target array to the necessary elements.) This breaks the principle of lean ponents.

I need to be able to register when a user clicks outside targeted elements. To do this I have created a custom attribute directive shown below.


ClickOutsideTargets Directive

import { Directive, ElementRef, Input, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[clickOutsideTargets]',
})
export class ClickOutsideTargetsDirective {

    @Input()
    public targets : HTMLElement[] = [];


    @Output() 
    clickOutsideTargets = new EventEmitter();

    constructor(){

    }


    @HostListener('document:click', ['$event.target'])
    public onClick(targetElement){

     if(!targetElement){
        return;
    }

    for(var i = 0, len = this.targets.length; i < len; i++){
        if(this.targets[i].contains(targetElement)){
            return;
        }
    }

    this.clickOutsideTargets.emit(null);

  }

}

The 'targets' array specifies the elements where the user can click without triggering an event. Whenever the user clicks outside these elements an event is emitted.


I want to be able to pass an array of targets to the directive as an input, using the following syntax which is clean and convenient. Each target is given as a template variable (#variable).

<button #buttonRef class="btn btn-primary" (click) = "activate()" (clickOutsideTargets) = "deactivate()" [targets] = "[buttonRef, containerRef]"> </button>



Issues with this approach

However, this doesn't work when-

  • One of the target elements exists in the template of a child ponent. Unless there is a way to access a child's template variables from the parent ponent's template.

  • One of the target elements is created dynamically, for example through an NgFor.


Is there a way to solve these issues? Or ideally, is there a better solution/strategy that I'm not considering.


(One really messsy solution to get the functionality is by creating a new template variable to represent the directive (using exportAs within the directive's decorator), accessing the directive within the parent ponent using ViewChild, accessing the target elements using ViewChild/ViewChildren and manually setting the directive's target array to the necessary elements.) This breaks the principle of lean ponents.

Share Improve this question asked Feb 13, 2018 at 15:50 Dom DDom D 211 silver badge4 bronze badges 1
  • This seems to help you stackoverflow./a/51152404/3176270 – Mohammad Kermani Commented Feb 20, 2020 at 9:30
Add a ment  | 

1 Answer 1

Reset to default 6

You might want to try this

In your Component

@ViewChild('myTarget') myTarget;

@HostListener('document:click', ['$event.target'])
    onClick(targetElement) {
        const clickedInside = this.myTarget.nativeElement.contains(targetElement);
        if (!clickedInside) {
            //the click was made outside of this element
        }
    }

In your html

<div #myTarget>
</div>
发布评论

评论列表(0)

  1. 暂无评论