외부 클릭시 드롭 다운을 어떻게 닫을 수 있습니까?


144

사용자가 해당 드롭 다운 외부의 아무 곳이나 클릭하면 로그인 메뉴 드롭 다운을 닫고 싶습니다. Angular2와 Angular2 "접근법"으로 수행하고 싶습니다 ...

솔루션을 구현했지만 실제로는 자신감이 없습니다. 나는 같은 결과를 얻는 가장 쉬운 방법이 있어야한다고 생각하므로 아이디어가 있다면 ... : 토론합시다!

내 구현은 다음과 같습니다.

드롭 다운 구성 요소 :

이것은 내 드롭 다운의 구성 요소입니다.

  • 이 컴포넌트가 표시되도록 설정할 때마다 (예 : 사용자가 버튼을 클릭하여 표시 할 때) SubjectsService 내에 저장된 "global"rxjs subject userMenu를 구독합니다 .
  • 그리고 숨길 때마다이 주제를 구독 취소합니다.
  • 이 구성 요소의 템플릿 어디에서나 클릭 할 때마다 onClick () 메서드가 트리거됩니다.이 메서드는 이벤트 버블 링을 맨 위 (및 응용 프로그램 구성 요소)로 중지합니다.

여기 코드가 있습니다

export class UserMenuComponent {

    _isVisible: boolean = false;
    _subscriptions: Subscription<any> = null;

    constructor(public subjects: SubjectsService) {
    }

    onClick(event) {
        event.stopPropagation();
    }

    set isVisible(v) {
        if( v ){
            setTimeout( () => {
this._subscriptions =  this.subjects.userMenu.subscribe((e) => {
                       this.isVisible = false;
                       })
            }, 0);
        } else {
            this._subscriptions.unsubscribe();
        }
        this._isVisible = v;
    }

    get isVisible() {
        return this._isVisible;
    }
}

응용 프로그램 구성 요소 :

반면에 응용 프로그램 구성 요소 (드롭 다운 구성 요소의 부모)가 있습니다.

  • 이 구성 요소는 모든 클릭 이벤트를 포착하고 동일한 rxjs 주제 ( userMenu )에서 생성합니다.

코드는 다음과 같습니다.

export class AppComponent {

    constructor( public subjects: SubjectsService) {
        document.addEventListener('click', () => this.onClick());
    }
    onClick( ) {
        this.subjects.userMenu.next({});
    }
}

나를 귀찮게하는 것 :

  1. 나는 그 구성 요소들 사이의 커넥터 역할을하는 글로벌 주제를 가지고 있다는 생각에 정말로 편안하지 않습니다.
  2. 에서는 setTimeout : 여기에 사용자가 버튼을 클릭 드롭 다운을 표시하는 경우, 그렇지 않으면 일이 무엇 때문이 필요합니다 :
    • 드롭 다운 구성 요소의 일부가 아닌 단추를 클릭하면 드롭 다운이 표시됩니다.
    • 드롭 다운이 표시되고 즉시 userMenu 주제를 구독합니다 .
    • 클릭 이벤트가 앱 구성 요소에 버블 링되어 붙잡음
    • 응용 프로그램 구성 요소가 userMenu 주제 에서 이벤트를 생성합니다.
    • 드롭 다운 컴포넌트는 userMenu 에서이 조치를 포착 하고 드롭 다운을 숨 깁니다.
    • 마지막에 드롭 다운이 표시되지 않습니다.

이 설정 시간 초과는 현재 JavaScript 코드 턴의 끝까지 구독을 지연시켜 문제를 해결하지만 매우 우아하게 생각합니다.

더 깨끗하고, 더 똑똑하고, 더 빠르거나 강한 솔루션을 알고 있다면 알려주세요 :)!


이 답변은 당신에게 몇 가지 아이디어를 줄 수 있습니다 : stackoverflow.com/a/35028820/215945 , stackoverflow.com/questions/35024495#35024651
Mark Rajcok

답변:


245

당신은 (document:click)이벤트 를 사용할 수 있습니다 :

@Component({
  host: {
    '(document:click)': 'onClick($event)',
  },
})
class SomeComponent() {
  constructor(private _eref: ElementRef) { }

  onClick(event) {
   if (!this._eref.nativeElement.contains(event.target)) // or some similar check
     doSomething();
  }
}

또 다른 방법은 사용자 지정 이벤트를 지시문으로 만드는 것입니다. Ben Nadel의 다음 게시물을 확인하십시오.


1
@Sasxa는 감사하고 동의했습니다. 더 이상 사용되지 않는 API 문서가 있다면 검색으로 표시되었을 것입니다.
danludwig 2016 년

4
event.target이 [innerHTML] 바인딩과 같은 것을 통해 동적으로 추가 된 요소 인 경우 elementRef의 nativeElement는이를 포함하지 않습니다.
Patrick Graham

8
이 기술의 유일한 단점은 이제 클릭 할 때마다 발생하는 클릭 이벤트 리스너가 애플리케이션에 있다는 것입니다.
codeepic

37
공식 Angular 2 스타일 가이드 에 따르면 데코레이터 @HostListener('document:click', ['$event'])host속성 대신 사용해야 합니다 Component.
Michał Miszczyszyn

15
또는 rxjs를 사용할 수도 Observable.fromEvent(document, 'click').subscribe(event => {your code here})있습니다. 예를 들어 드롭 다운을 연 예를 들어 청취해야 할 때와 구독을 취소 할 때만 구독 할 수 있습니다.
Blind Despair

42

우아한 방법

https://github.com/chliebel/angular2-click-outsideclickOut 지시어를 찾았습니다 . 확인하고 제대로 작동합니다 ( 프로젝트 에만 복사 ). U는 다음과 같이 사용할 수 있습니다.clickOutside.directive.ts

<div (clickOutside)="close($event)"></div>

close사용자가 div 외부를 클릭하면 호출 될 함수는 어디에 있습니까 ? 문제가 설명 된 문제를 처리하는 매우 우아한 방법입니다.

위의 지시문을 사용하여 팝업 창을 닫는 경우 먼저 event.stopPropagation()팝업을 여는 단추 클릭 이벤트 핸들러 에 추가 해야합니다.

보너스:

아래에서 파일에서 oryginal 지시어 코드를 복사합니다 clickOutside.directive.ts(향후 링크가 작동하지 않을 경우). 저자는 Christian Liebel입니다 .


2
@Vega 내 추천은 * ngIf가있는 요소에서 지시문을 사용하는 것입니다. 드롭 다운의 경우 다음과 같습니다.<div class="wrap" *ngIf="isOpened" (clickOutside)="...// this should set this.isOpen=false"
Gabriel Balsa Cantú

19

나는 이렇게했다.

문서 click와 해당 핸들러 에 이벤트 리스너를 추가하여 내 container포함 여부를 확인했습니다 event.target. 드롭 다운을 숨 깁니다.

이렇게 보일 것입니다.

@Component({})
class SomeComponent {
    @ViewChild('container') container;
    @ViewChild('dropdown') dropdown;

    constructor() {
        document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
    }

    offClickHandler(event:any) {
        if (!this.container.nativeElement.contains(event.target)) { // check click origin
            this.dropdown.nativeElement.style.display = "none";
        }
    }
}

안녕하세요. 바인드 (이)가 필요합니까?
Drenai

1
@Brian 필요할 수도 있고 아닐 수도 있지만 this.offClickHandler화살표 함수로 래핑 한 경우에는 그렇지 않습니다 .
Lansana Camara

17

나는 Sasxa가 대부분의 사람들에게 대답을 받아 들였다고 생각합니다. 그러나 오프 클릭 이벤트를 수신 해야하는 Element의 내용이 동적으로 변경되는 상황이있었습니다. 따라서 Elements nativeElement는 동적으로 작성된 event.target을 포함하지 않았습니다. 다음 지시문으로 해결할 수 있습니다

@Directive({
  selector: '[myOffClick]'
})
export class MyOffClickDirective {

  @Output() offClick = new EventEmitter();

  constructor(private _elementRef: ElementRef) {
  }

  @HostListener('document:click', ['$event.path'])
  public onGlobalClick(targetElementPath: Array<any>) {
    let elementRefInPath = targetElementPath.find(e => e === this._elementRef.nativeElement);
    if (!elementRefInPath) {
      this.offClick.emit(null);
    }
  }
}

elementRef에 event.target이 포함되어 있는지 확인하는 대신 elementRef가 이벤트의 경로 (DOM 경로)에 있는지 확인합니다. 이렇게하면 동적으로 생성 된 요소를 처리 할 수 ​​있습니다.


감사합니다-자식 구성 요소가있을 때 더 잘 작동합니다
MAhsan

이것은 나에게 많은 도움이되었다. 구성 요소 외부 클릭이 다른 답변으로 감지되지 않는 이유를 잘 모르겠습니다.
JavaQuest 2009 년

13

iOS에서이 작업을 수행하는 경우 touchstart 이벤트도 .

Angular 4부터는 HostListener꾸미기가 선호되는 방법입니다.

import { Component, OnInit, HostListener, ElementRef } from '@angular/core';
...
@Component({...})
export class MyComponent implement OnInit {

  constructor(private eRef: ElementRef){}

  @HostListener('document:click', ['$event'])
  @HostListener('document:touchstart', ['$event'])
  handleOutsideClick(event) {
    // Some kind of logic to exclude clicks in Component.
    // This example is borrowed Kamil's answer
    if (!this.eRef.nativeElement.contains(event.target) {
      doSomethingCool();
    }
  }

}

10

우리는 오늘 직장에서 비슷한 문제를 해결하기 위해 드롭 다운 div를 클릭하면 사라지는 방법을 알아 내려고 노력했습니다. 우리는 다른 구성 요소 또는 지시문 을 클릭하지 않고 특정 div 외부 에서 클릭하기를 원하지 않기 때문에 초기 포스터의 질문과 약간 다릅니다 .

우리는 (window : mouseup) 이벤트 핸들러를 사용하여 문제를 해결했습니다.

단계 :
1.) 전체 드롭 다운 메뉴 div에 고유 한 클래스 이름을 지정했습니다.

2.) 내부 드롭 다운 메뉴 자체 (메뉴를 닫지 않기 위해 클릭하려는 유일한 부분)에 (window : mouseup) 이벤트 핸들러를 추가하고 $ event에 전달했습니다.

참고 : 일반적인 "클릭"처리기는 부모 클릭 처리기와 충돌하기 때문에 수행 할 수 없습니다.

3.) 컨트롤러에서, 우리는 클릭 아웃 이벤트에서 호출되기를 원하는 메소드를 만들었고 , 클릭 된 스팟이 타겟 클래스 div 내에 있는지 알아 내기 위해 event.closest ( docs here )를 사용합니다 .

 autoCloseForDropdownCars(event) {
        var target = event.target;
        if (!target.closest(".DropdownCars")) { 
            // do whatever you want here
        }
    }
 <div class="DropdownCars">
   <span (click)="toggleDropdown(dropdownTypes.Cars)" class="searchBarPlaceholder">Cars</span>
   <div class="criteriaDropdown" (window:mouseup)="autoCloseForDropdownCars($event)" *ngIf="isDropdownShown(dropdownTypes.Cars)">
   </div>
</div>


호스트 데코레이터 내에서 "window : mouseup"을 사용해야합니다.
Shivam

@ Shivam-- "호스트 데코레이터 내에서 사용해야한다"는 무슨 뜻인지 잘 모르겠습니다. 더 자세히 설명해 주시겠습니까? 감사!
Paige Bolduc

"window"오브젝트를 직접 사용하는 대신 컴포넌트 데코레이터의 "host"속성 / 컴포넌트의 "HostListener"데코레이터를 사용해야합니다. 각도 2에서 "창"또는 "문서"개체로 작업하는 동안 이것이 표준 관행입니다.
Shivam

2
브라우저 호환성에 주목하십시오. .closest()현재 IE / Edge에서 지원되지 않습니다 ( caniuse )
superjos

5

드롭 다운에 형제 요소를 만들어 보이지 않는 전체 화면을 덮고 클릭 이벤트를 캡처 할 수 있습니다. 그런 다음 해당 요소의 클릭을 감지하고 클릭하면 드롭 다운을 닫을 수 있습니다. 요소가 실크 스크린 클래스라고 가정 해 보겠습니다. 여기에 스타일이 있습니다.

.silkscreen {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1;
}

z- 색인은 드롭 다운을 제외한 모든 것 위에 배치하기에 충분히 높아야합니다. 이 경우 내 드롭 다운은 z-index 2입니다.

다른 답변은 경우에 따라 효과가 있었지만 때로는 요소 내의 요소와 상호 작용할 때 내 드롭 다운이 닫히고 싶지 않았기 때문에 제외했습니다. 예상대로 이벤트 대상에 따라 구성 요소에 포함되지 않은 요소를 동적으로 추가했습니다. 그 엉망을 분류하는 대신 실크 스크린 방식으로 시도해 보았습니다.


5

나는 해결책을 찾지 않았다. 방금 문서를 첨부했습니다 : 다음과 같이 토글 기능을 클릭하십시오.

    @지령({
      선택기 : '[appDropDown]'
    })
    내보내기 클래스 DropdownDirective는 OnInit {

      @HostBinding ( 'class.open') isOpen : 부울;

      생성자 (비공개 elemRef : ElementRef) {}

      ngOnInit () : 무효 {
        this.isOpen = 거짓;
      }

      @HostListener ( 'document : click', [ '$ event'])
      @HostListener ( 'document : touchstart', [ '$ event'])
      토글 (이벤트) {
        if (this.elemRef.nativeElement.contains (event.target)) {
          this.isOpen =! this.isOpen;
        } else {
          this.isOpen = 거짓;
      }
    }

따라서 지시문을 벗어나면 드롭 다운을 닫습니다.


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

@Component({
    selector: 'custom-dropdown',
    template: `
        <div class="custom-dropdown-container">
            Dropdown code here
        </div>
    `
})
export class CustomDropdownComponent {
    thisElementClicked: boolean = false;

    constructor() { }

    @HostListener('click', ['$event'])
    onLocalClick(event: Event) {
        this.thisElementClicked = true;
    }

    @HostListener('document:click', ['$event'])
    onClick(event: Event) {
        if (!this.thisElementClicked) {
            //click was outside the element, do stuff
        }
        this.thisElementClicked = false;
    }
}

설명 :-페이지의 이러한 구성 요소마다 클릭 이벤트 리스너 2 개. 페이지에 수백 번있는 구성 요소에는 이것을 사용하지 마십시오.


아니요, 데스크톱 브라우저에서만 사용했습니다.
Alex Egli

3

구성 요소 외부를 클릭 한 후에 이벤트가 제거되지 않기 때문에 @Tony 답변을 보완하고 싶습니다. 영수증 완료 :

  • #container로 주요 ​​요소 표시

    @ViewChild('container') container;
    
    _dropstatus: boolean = false;
    get dropstatus() { return this._dropstatus; }
    set dropstatus(b: boolean) 
    {
        if (b) { document.addEventListener('click', this.offclickevent);}
        else { document.removeEventListener('click', this.offclickevent);}
        this._dropstatus = b;
    }
    offclickevent: any = ((evt:any) => { if (!this.container.nativeElement.contains(evt.target)) this.dropstatus= false; }).bind(this);
  • 클릭 가능한 요소에서 다음을 사용하십시오.

    (click)="dropstatus=true"

이제 dropstatus 변수를 사용하여 드롭 다운 상태를 제어하고 [ngClass]를 사용하여 적절한 클래스를 적용 할 수 있습니다.


3

지시어를 작성할 수 있습니다.

@Directive({
  selector: '[clickOut]'
})
export class ClickOutDirective implements AfterViewInit {
  @Input() clickOut: boolean;

  @Output() clickOutEvent: EventEmitter<any> = new EventEmitter<any>();

  @HostListener('document:mousedown', ['$event']) onMouseDown(event: MouseEvent) {

       if (this.clickOut && 
         !event.path.includes(this._element.nativeElement))
       {
           this.clickOutEvent.emit();
       }
  } 


}

컴포넌트에서 :

@Component({
  selector: 'app-root',
  template: `
    <h1 *ngIf="isVisible" 
      [clickOut]="true" 
      (clickOutEvent)="onToggle()"
    >{{title}}</h1>
`,
  styleUrls: ['./app.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  title = 'app works!';

  isVisible = false;

  onToggle() {
    this.isVisible = !this.isVisible;
  }
}

이 지시문은 html 요소가 DOM에 포함되어 있고 [clickOut] 입력 특성이 'true'인 경우 이벤트를 생성합니다. DOM에서 요소가 제거되기 전에 이벤트를 처리하기 위해 mousedown 이벤트를 수신합니다.

참고 사항 : firefox에는 이벤트에 'path'속성이 포함되어 있지 않습니다. 함수를 사용하여 경로를 만들 수 있습니다.

const getEventPath = (event: Event): HTMLElement[] => {
  if (event['path']) {
    return event['path'];
  }
  if (event['composedPath']) {
    return event['composedPath']();
  }
  const path = [];
  let node = <HTMLElement>event.target;
  do {
    path.push(node);
  } while (node = node.parentElement);
  return path;
};

따라서 지시문에서 이벤트 핸들러를 변경해야합니다. event.path는 getEventPath (event)로 대체되어야합니다.

이 모듈이 도움이 될 수 있습니다. https://www.npmjs.com/package/ngx-clickout 동일한 논리를 포함하지만 소스 html 요소에서 esc 이벤트를 처리합니다.


3

정답은 문제가 있습니다. 팝 오버에 clicakble 구성 요소가 있으면 요소가 더 이상 contain메소드 에 있지 않고 @ JuHarm89에 따라 닫힙니다. 내가 직접 만들었습니다.

export class PopOverComponent implements AfterViewInit {
 private parentNode: any;

  constructor(
    private _element: ElementRef
  ) { }

  ngAfterViewInit(): void {
    this.parentNode = this._element.nativeElement.parentNode;
  }

  @HostListener('document:click', ['$event.path'])
  onClickOutside($event: Array<any>) {
    const elementRefInPath = $event.find(node => node === this.parentNode);
    if (!elementRefInPath) {
      this.closeEventEmmit.emit();
    }
  }
}

도와 주셔서 감사합니다!


2

@Tony 훌륭한 솔루션을위한 더 나은 버전 :

@Component({})
class SomeComponent {
    @ViewChild('container') container;
    @ViewChild('dropdown') dropdown;

    constructor() {
        document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
    }

    offClickHandler(event:any) {
        if (!this.container.nativeElement.contains(event.target)) { // check click origin

            this.dropdown.nativeElement.closest(".ourDropdown.open").classList.remove("open");

        }
    }
}

CSS 파일에서 : // 부트 스트랩 드롭 다운을 사용하는 경우 필요하지 않습니다.

.ourDropdown{
   display: none;
}
.ourDropdown.open{
   display: inherit;
}

2

대신 모달 오버레이를 클릭하면 훨씬 쉽게 확인할 수 있습니다.

템플릿 :

<div #modalOverlay (click)="clickOutside($event)" class="modal fade show" role="dialog" style="display: block;">
        <div class="modal-dialog" [ngClass]='size' role="document">
            <div class="modal-content" id="modal-content">
                <div class="close-modal" (click)="closeModal()"> <i class="fa fa-times" aria-hidden="true"></i></div>
                <ng-content></ng-content>
            </div>
        </div>
    </div>

그리고 방법 :

  @ViewChild('modalOverlay') modalOverlay: ElementRef;

// ... your constructor and other method

      clickOutside(event: Event) {
    const target = event.target || event.srcElement;
    console.log('click', target);
    console.log("outside???", this.modalOverlay.nativeElement == event.target)
    // const isClickOutside = !this.modalBody.nativeElement.contains(event.target);
    // console.log("click outside ?", isClickOutside);
    if ("isClickOutside") {
      // this.closeModal();
    }


  }

2

이 비슷한 문제를 해결하기 위해 지시문을 작성했으며 Bootstrap을 사용하고 있습니다. 그러나 필자의 경우 현재 열린 드롭 다운 메뉴를 닫기 위해 요소 외부의 클릭 이벤트를 기다리는 대신 메뉴를 자동으로 닫는 'mouseleave'이벤트를 살펴 보는 것이 좋습니다.

내 해결책은 다음과 같습니다.

지령

import { Directive, HostListener, HostBinding } from '@angular/core';
@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {

  @HostBinding('class.open') isOpen = false;

  @HostListener('click') toggleOpen() {
    this.isOpen = !this.isOpen;
  }

  @HostListener('mouseleave') closeDropdown() {
    this.isOpen = false;
  }

}

HTML

<ul class="nav navbar-nav navbar-right">
    <li class="dropdown" appDropdown>
      <a class="dropdown-toggle" data-toggle="dropdown">Test <span class="caret"></span>
      </a>
      <ul class="dropdown-menu">
          <li routerLinkActive="active"><a routerLink="/test1">Test1</a></li>
          <li routerLinkActive="active"><a routerLink="/test2/">Test2</a></li>
      </ul>
    </li>
</ul>

1

부트 스트랩을 사용하는 경우 드롭 다운 (부트 스트랩 구성 요소)을 통해 부트 스트랩 방식으로 직접 수행 할 수 있습니다.

<div class="input-group">
    <div class="input-group-btn">
        <button aria-expanded="false" aria-haspopup="true" class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">
            Toggle Drop Down. <span class="fa fa-sort-alpha-asc"></span>
        </button>
        <ul class="dropdown-menu">
            <li>List 1</li>
            <li>List 2</li>
            <li>List 3</li>
        </ul>
    </div>
</div>

이제 (click)="clickButton()"버튼 에 물건 을 넣는 것이 좋습니다. http://getbootstrap.com/javascript/#dropdowns


1

나는 또한 내 자신의 작은 해결 방법을 수행했습니다.

ng-select 요소 구성 요소를 듣고 현재 열려있는 SelectComponent와는 별도로 열려있는 다른 SelectComponent를 모두 닫는 함수를 호출 하는 (dropdownOpen) 이벤트를 만들었 습니다.

select.ts 파일 내에서 하나의 함수를 수정하여 아래와 같이 이벤트를 생성했습니다.

private open():void {
    this.options = this.itemObjects
        .filter((option:SelectItem) => (this.multiple === false ||
        this.multiple === true && !this.active.find((o:SelectItem) => option.text === o.text)));

    if (this.options.length > 0) {
        this.behavior.first();
    }
    this.optionsOpened = true;
    this.dropdownOpened.emit(true);
}

HTML에서 (dropdownOpened)에 대한 이벤트 리스너를 추가했습니다 .

<ng-select #elem (dropdownOpened)="closeOtherElems(elem)"
    [multiple]="true"
    [items]="items"
    [disabled]="disabled"
    [isInputAllowed]="true"
    (data)="refreshValue($event)"
    (selected)="selected($event)"
    (removed)="removed($event)"
    placeholder="No city selected"></ng-select>

이것은 ng2-select 태그가있는 구성 요소 내부의 이벤트 트리거에 대한 호출 함수입니다.

@ViewChildren(SelectComponent) selectElem :QueryList<SelectComponent>;

public closeOtherElems(element){
    let a = this.selectElem.filter(function(el){
                return (el != element)
            });

    a.forEach(function(e:SelectComponent){
        e.closeDropdown();
    })
}

1

참고 : 웹 워커를 사용하려는 경우 문서와 nativeElement를 사용하지 않아야합니다.

나는 같은 질문에 대답했다 : https : //.com/questions/47571144

위 링크에서 복사 / 붙여 넣기 :

드롭 다운 메뉴를 만들 때와 동일한 문제가 있었고 외부를 클릭 할 때 확인 대화 상자를 닫고 싶었습니다.

내 최종 구현은 완벽하게 작동하지만 CSS3 애니메이션과 스타일이 필요합니다.

참고 : 아래 코드를 테스트하지 않았으므로 해결해야 할 구문 문제가있을 수 있으며 자신의 프로젝트에 대한 명백한 조정이 있습니다!

제가 한:

높이 100 %, 너비 100 % 및 변환 : scale (0)으로 별도의 고정 된 div를 만들었습니다. 이것은 본질적으로 배경이며 배경색으로 스타일을 지정할 수 있습니다. rgba (0, 0, 0, 0.466); 메뉴가 열려 있고 배경이 클릭하여 닫히는 것을 분명히합니다. 메뉴는 다른 것보다 z- 색인을 높인 다음 배경 div는 메뉴보다 z- 색인을 낮추지 만 다른 것보다 높은 z- 색인을 가져옵니다. 그런 다음 배경에는 클릭 이벤트가있어 드롭 다운을 닫습니다.

여기 HTML 코드가 있습니다.

<div class="dropdownbackground" [ngClass]="{showbackground: qtydropdownOpened}" (click)="qtydropdownOpened = !qtydropdownOpened"><div>
<div class="zindex" [class.open]="qtydropdownOpened">
  <button (click)="qtydropdownOpened = !qtydropdownOpened" type="button" 
         data-toggle="dropdown" aria-haspopup="true" [attr.aria-expanded]="qtydropdownOpened ? 'true': 'false' ">
   {{selectedqty}}<span class="caret margin-left-1x "></span>
 </button>
  <div class="dropdown-wrp dropdown-menu">
  <ul class="default-dropdown">
      <li *ngFor="let quantity of quantities">
       <a (click)="qtydropdownOpened = !qtydropdownOpened;setQuantity(quantity)">{{quantity  }}</a>
       </li>
   </ul>
  </div>
 </div>

다음은 간단한 애니메이션이 필요한 css3입니다.

/* make sure the menu/drop-down is in front of the background */
.zindex{
    z-index: 3;
}

/* make background fill the whole page but sit behind the drop-down, then
scale it to 0 so its essentially gone from the page */
.dropdownbackground{
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 2;
    transform: scale(0);
    opacity: 0;
    background-color: rgba(0, 0, 0, 0.466);
}

/* this is the class we add in the template when the drop down is opened
it has the animation rules set these how you like */
.showbackground{
    animation: showBackGround 0.4s 1 forwards; 

}

/* this animates the background to fill the page
if you don't want any thing visual you could use a transition instead */
@keyframes showBackGround {
    1%{
        transform: scale(1);
        opacity: 0;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}

시각적 인 것이 없다면 다음과 같은 전환을 사용할 수 있습니다.

.dropdownbackground{
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 2;
    transform: scale(0);
    opacity: 0;
    transition all 0.1s;
}

.dropdownbackground.showbackground{
     transform: scale(1);
}

1

포커스 / 블러 이벤트가있는 예제에서 영감을 얻은 다른 솔루션을 발견했습니다.

따라서 글로벌 문서 리스너를 첨부하지 않고 동일한 기능을 달성하려는 경우 다음 예제를 유효한 것으로 간주 할 수 있습니다. 다른 버튼 포커스 이벤트 처리에도 불구하고 OSx의 Safari 및 Firefox에서도 작동합니다. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus

각도 8이있는 stackbiz의 작업 예 : https://stackblitz.com/edit/angular-sv4tbi?file=src%2Ftoggle-dropdown%2Ftoggle-dropdown.directive.ts

HTML 마크 업 :

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button" aria-haspopup="true" aria-expanded="false">Dropdown button</button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#">Action</a>
    <a class="dropdown-item" href="#">Another action</a>
    <a class="dropdown-item" href="#">Something else here</a>
  </div>
</div>

지시문은 다음과 같습니다.

import { Directive, HostBinding, ElementRef, OnDestroy, Renderer2 } from '@angular/core';

@Directive({
  selector: '.dropdown'
})
export class ToggleDropdownDirective {

  @HostBinding('class.show')
  public isOpen: boolean;

  private buttonMousedown: () => void;
  private buttonBlur: () => void;
  private navMousedown: () => void;
  private navClick: () => void;

  constructor(private element: ElementRef, private renderer: Renderer2) { }

  ngAfterViewInit() {
    const el = this.element.nativeElement;
    const btnElem = el.querySelector('.dropdown-toggle');
    const menuElem = el.querySelector('.dropdown-menu');

    this.buttonMousedown = this.renderer.listen(btnElem, 'mousedown', (evt) => {
      console.log('MOUSEDOWN BTN');
      this.isOpen = !this.isOpen;
      evt.preventDefault(); // prevents loose of focus (default behaviour) on some browsers
    });

    this.buttonMousedown = this.renderer.listen(btnElem, 'click', () => {
      console.log('CLICK BTN');
      // firefox OSx, Safari, Ie OSx, Mobile browsers.
      // Whether clicking on a <button> causes it to become focused varies by browser and OS.
      btnElem.focus();
    });

    // only for debug
    this.buttonMousedown = this.renderer.listen(btnElem, 'focus', () => {
      console.log('FOCUS BTN');
    });

    this.buttonBlur = this.renderer.listen(btnElem, 'blur', () => {
      console.log('BLUR BTN');
      this.isOpen = false;
    });

    this.navMousedown = this.renderer.listen(menuElem, 'mousedown', (evt) => {
      console.log('MOUSEDOWN MENU');
      evt.preventDefault(); // prevents nav element to get focus and button blur event to fire too early
    });
    this.navClick = this.renderer.listen(menuElem, 'click', () => {
      console.log('CLICK MENU');
      this.isOpen = false;
      btnElem.blur();
    });
  }

  ngOnDestroy() {
    this.buttonMousedown();
    this.buttonBlur();
    this.navMousedown();
    this.navClick();
  }
}

1

mouseleave이처럼 당신의 관점에서 사용할 수 있습니다

각도 8로 테스트하고 완벽하게 작동

<ul (mouseleave)="closeDropdown()"> </ul>

마우스가 떠날 때 컨테이너가 닫히지 만 어쨌든 내가 그 존재를 알지 못했기 때문에 공유해 주셔서 감사합니다.
벤 헤이워드

0

가장 우아한 방법 : D

이를 수행하는 가장 쉬운 방법이 하나 있으며 지시어가 필요하지 않습니다.

"요소-드롭 다운 메뉴"는 버튼 태그 여야합니다. (흐림) 속성에 모든 방법을 사용하십시오. 그게 다야.

<button class="element-that-toggle-your-dropdown"
               (blur)="isDropdownOpen = false"
               (click)="isDropdownOpen = !isDropdownOpen">
</button>

당신은 예를 들어, 사용자가 버튼을 클릭 놓칠 수, 클릭에 열려있는 드롭 다운을 유지하려면이 늘 일
Yeswhen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.