외부 클릭 시 드롭다운을 닫으려면 어떻게 해야 합니까?
사용자가 해당 드롭다운 이외의 위치를 클릭할 때 로그인 메뉴 드롭다운을 닫고, Angular2 및 Angular2 "접근"을 사용하고 싶습니다.
나는 해결책을 실행했지만, 정말로 그것에 자신이 없다.같은 결과를 얻을 수 있는 가장 쉬운 방법이 있을 것 같기 때문에 뭔가 아이디어가 있으면...논의합시다:)!
실장은 다음과 같습니다.
드롭다운 컴포넌트:
드롭다운의 컴포넌트는 다음과 같습니다.
- 이 컴포넌트가 표시되도록 설정될 때마다(예를 들어 사용자가 버튼을 클릭하여 표시할 때) Subjects Service에 저장된 "글로벌" rxjs 서브젝트 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 Subject(userMenu)로 송신합니다.
코드는 다음과 같습니다.
export class AppComponent {
constructor( public subjects: SubjectsService) {
document.addEventListener('click', () => this.onClick());
}
onClick( ) {
this.subjects.userMenu.next({});
}
}
신경이 쓰이는 점:
- 이러한 컴포넌트간의 연결고리로서 기능하는 글로벌 서브젝트를 가지는 것은, 그다지 마음에 들지 않습니다.
- set Timeout:사용자가 드롭다운을 표시하는 버튼을 클릭하면 다음과 같은 상황이 발생하므로 이 작업이 필요합니다.
- 드롭다운 컴포넌트의 일부가 아닌 버튼을 클릭하여 드롭다운을 표시합니다.
- 드롭다운이 표시되고 userMenu 제목에 즉시 등록됩니다.
- 클릭 이벤트가 앱 컴포넌트까지 버블이 되어 잡힙니다.
- 응용 프로그램 구성 요소가 userMenu 제목에 대한 이벤트를 내보냅니다.
- 드롭다운 구성 요소는 userMenu에서 이 작업을 캡처하여 드롭다운을 숨깁니다.
- 마지막에 드롭다운이 표시되지 않습니다.
이 설정된 타임아웃은 문제를 해결하는 현재 JavaScript 코드 턴의 마지막까지 구독을 지연시키고 있지만, 제 생각에는 매우 우아하게 보입니다.
보다 깨끗하고, 보다 우수하고, 보다 스마트하고, 보다 빠르고, 보다 강력한 솔루션을 알고 있다면 알려주세요.
하시면 됩니다.(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의 투고를 확인해 주세요.
우아한 방법
는 이것을 했다.clickOut
디렉티브 : https://github.com/chliebel/angular2-click-outside확인해보니 잘 작동합니다(복사만 합니다).clickOutside.directive.ts
과 같이할 수 .을 사용하다
<div (clickOutside)="close($event)"></div>
서 ★★★★★close
외음부이 방법은 문제의 문제를 다루는 매우 우아한 방법입니다.
창을 닫는 popUp을 합니다.event.stopPropagation()
버튼을 클릭하여 popUp을 여는 이벤트 핸들러를 클릭합니다.
보너스:
지시 를 oryginal 파일에서 합니다.clickOutside.directive.ts
(앞으로 링크가 작동하지 않을 경우) - 저자는 Christian Libel:
import {Directive, ElementRef, Output, EventEmitter, HostListener} from '@angular/core';
@Directive({
selector: '[clickOutside]'
})
export class ClickOutsideDirective {
constructor(private _elementRef: ElementRef) {
}
@Output()
public clickOutside = new EventEmitter<MouseEvent>();
@HostListener('document:click', ['$event', '$event.target'])
public onClick(event: MouseEvent, targetElement: HTMLElement): void {
if (!targetElement) {
return;
}
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!clickedInside) {
this.clickOutside.emit(event);
}
}
}
난 이렇게 해왔어.
에 했습니다.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";
}
}
}
나는 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 경로)에 있는지 확인합니다.이렇게 하면 동적으로 작성된 요소를 처리할 수 있습니다.
이 iOS를 하십시오.touchstart
이치노
4 Angular 는 Angular 입니다.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();
}
}
}
우리는 오늘 직장에서 비슷한 이슈에 대해 연구하고 있는데, 클릭했을 때 드롭다운 div가 사라지도록 하는 방법을 알아내기 위해 노력하고 있다.우리의 질문은 처음 포스터의 질문과는 조금 다릅니다.왜냐하면 우리는 다른 컴포넌트나 디렉티브를 클릭하는 것이 아니라 단지 특정 div를 클릭하는 것을 원하지 않았기 때문입니다.
(window:mouseup) 이벤트 핸들러를 사용하여 해결했습니다.
★★★★
이름을.1) 에운운 운운 1 div 。
2) 자체 하는 .2)를 했습니다(window:mouseup).
메모: 일반적인 "클릭" 핸들러에서는 실행할 수 없습니다.이것은 부모 클릭 핸들러와 경합하기 때문입니다.
3) 컨트롤러에서는 클릭아웃 이벤트에서 호출하고 싶은 메서드를 만들고 event.closest(여기 docs)를 사용하여 클릭된 지점이 타깃 클래스 div 내에 있는지 확인합니다.
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>
하시면 됩니다.mouseleave
각도 8로 테스트하여 완벽하게 작동
<ul (mouseleave)="closeDropdown()"> </ul>
나는 어떤 해결책도 만들지 않았다.문서를 첨부했습니다.다음과 같이 토글 기능을 클릭합니다.
@지시({선택기: '[appDropDown]'})export 클래스 Dropdown Directive는 OnInit {를 구현합니다. @HostBinding('class.open') isOpen: 부울; 컨스트럭터(프라이빗 elemRef: ElementRef) { } ngOnInit(): 무효 {this.isOpen = false;} @HostListener('document: click', [$event') @HostListener('문서: touchstart', [$event]) 전환(이벤트) {( this . elemRef . nativeElement . contains ( event . target ) )의 경우,this.isOpen = !this.isOpen;} 기타 {this.isOpen = false;}}
그래서 지시사항을 벗어나면 드롭다운을 닫습니다.
드롭다운에 형제 요소를 만들 수 있습니다. 형제 요소는 화면 전체를 덮으며 클릭 이벤트만 캡처할 수 있습니다.그런 다음 해당 요소의 클릭을 감지하고 클릭할 때 드롭다운을 닫을 수 있습니다.이 요소가 클래스 실크스크린이라고 가정해 보겠습니다.그 스타일은 다음과 같습니다.
.silkscreen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
}
z-index는 드롭다운을 제외한 모든 항목 위에 배치할 수 있을 정도로 높아야 합니다.이 경우 드롭다운은 b z-index 2가 됩니다.
다른 답변은 경우에 따라서는 효과가 있었습니다만, 그 안에 있는 요소와 상호작용을 하면 드롭다운이 닫히는 경우가 있었습니다.예상대로 이벤트 대상에 따라 컴포넌트에 포함되지 않은 요소를 동적으로 추가했습니다.그 난장판을 정리하기보다는 실크스크린 방식으로 해볼까 생각했어요.
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;
}
}
DONSIDES: - 페이지에 있는 각 컴포넌트에 대해 이벤트 청취자를 두 번 클릭합니다.페이지에 있는 컴포넌트에는 수백 번 사용하지 마십시오.
컴포넌트 외부를 클릭해도 이벤트가 삭제되지 않기 때문에 @TONY 답변을 보완하고 싶습니다.수신 확인 완료:
#컨테이너로 주요 요소에 표시
@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"
이제 드롭 상태 변수를 사용하여 드롭다운 상태를 제어하고 [ngClass]를 사용하여 적절한 클래스를 적용할 수 있습니다.
지시문을 작성할 수 있습니다.
@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에 포함되어 있을 때 및 [클릭]할 때 이벤트를 내보냅니다.Out] 입력 속성이 'true'입니다.요소가 DOM에서 제거되기 전에 이벤트를 처리하기 위해 마우스 다운 이벤트를 수신합니다.
또한 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(이벤트)로 대체해야 합니다.
이 모듈이 도움이 됩니다.https://www.npmjs.com/package/ngx-clickout 이것은 같은 논리를 포함하고 있지만 소스 html 요소의 esc 이벤트도 처리합니다.
문제가 더 on정에 the the the the the the the the the the the 에 없습니다. 팝오버에 진부한 컴포넌트가 있는 경우 요소는 더 이상 해당되지 않습니다.contain
는 @ method 및 method my my my @JuHarm89 i 는 @ @ @ @ @ @ @ @ @ @ @ @ @ @ 。
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();
}
}
}
도와줘서 고마워요!
대신 모달 오버레이를 클릭하면 훨씬 쉽게 확인할 수 있습니다.
템플릿:
<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 methods
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();
}
}
@Tony great 솔루션에 적합한 버전:
@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;
}
나는 답이 충분하지 않다고 생각했기 때문에 동참하고 싶다.내가 한 일은 이렇다.
컴포넌트.ts 를 참조해 주세요.
@Component({
selector: 'app-issue',
templateUrl: './issue.component.html',
styleUrls: ['./issue.component.sass'],
})
export class IssueComponent {
@Input() issue: IIssue;
@ViewChild('issueRef') issueRef;
public dropdownHidden = true;
constructor(private ref: ElementRef) {}
public toggleDropdown($event) {
this.dropdownHidden = !this.dropdownHidden;
}
@HostListener('document:click', ['$event'])
public hideDropdown(event: any) {
if (!this.dropdownHidden && !this.issueRef.nativeElement.contains(event.target)) {
this.dropdownHidden = true;
}
}
}
component.displaces(컴포넌트).
<div #issueRef (click)="toggleDropdown()">
<div class="card card-body">
<p class="card-text truncate">{{ issue.fields.summary }}</p>
<div class="d-flex justify-content-between">
<img
*ngIf="issue.fields.assignee; else unassigned"
class="rounded"
[src]="issue.fields.assignee.avatarUrls['32x32']"
[alt]="issue.fields.assignee.displayName"
/>
<ng-template #unassigned>
<img
class="rounded"
src="https://img.icons8.com/pastel-glyph/2x/person-male--v2.png"
alt="Unassigned"
/>
</ng-template>
<img
*ngIf="issue.fields.priority"
class="rounded mt-auto priority"
[src]="issue.fields.priority.iconUrl"
[alt]="issue.fields.priority.name"
/>
</div>
</div>
<div *ngIf="!dropdownHidden" class="list-group context-menu">
<a href="#" class="list-group-item list-group-item-action active" aria-current="true">
The current link item
</a>
<a href="#" class="list-group-item list-group-item-action">A second link item</a>
<a href="#" class="list-group-item list-group-item-action">A third link item</a>
<a href="#" class="list-group-item list-group-item-action">A fourth link item</a>
<a
href="#"
class="list-group-item list-group-item-action disabled"
tabindex="-1"
aria-disabled="true"
>A disabled link item</a
>
</div>
</div>
부트스트랩을 사용하는 경우 드롭다운(부트스트랩컴포넌트)을 통해 부트스트랩 방식으로 직접 실행할 수 있습니다.
<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>
, 이제 now어넣 put put put put now now now 、 now now now now now 。(click)="clickButton()"
버튼에 있는 것.http://getbootstrap.com/javascript/ #syslogs
저 혼자 해결도 좀 해봤어요.
ng-select 요소 컴포넌트에서 듣고 현재 열려 있는 Select Component 이외의 다른 모든 Select Component를 닫는 함수를 호출하는 (드롭다운 열기) 이벤트를 만들었습니다.
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();
})
}
메모: 웹 워커를 사용하고 싶은 사용자로 문서와 native Element를 사용하지 않는 것이 좋습니다.
같은 질문에 답했습니다.https://stackoverflow.com/questions/47571144
위 링크에서 복사/붙여넣기:
드롭 다운 메뉴와 확인 다이얼로그를 작성할 때도, 「외부」를 클릭했을 때에 해제하고 싶은 문제가 있었습니다.
최종 구현은 완벽하게 작동하지만 css3 애니메이션과 스타일링이 필요합니다.
메모: 아래 코드는 테스트하지 않았습니다.구문에 문제가 있을 수 있습니다.또, 고객 자신의 프로젝트를 위해서도 명백한 조정이 필요합니다.
내가 한 일:
높이가 100%, 폭이 100%, 변환: scale(0), 기본적으로 배경입니다.rgba(0, 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);
}
가장 우아한 방법: D
그렇게 하는 가장 쉬운 방법이 하나 있는데, 그에 대한 지시는 필요 없습니다.
"그-그-그-그-그-그-그-그-그-그-그-그-그-그-그-그(blur) 속성에는 임의의 메서드를 사용합니다.그게 다예요.
<button class="element-that-toggle-your-dropdown"
(blur)="isDropdownOpen = false"
(click)="isDropdownOpen = !isDropdownOpen">
</button>
포커스/블러 이벤트에 관한 예에서 영감을 얻어 다른 솔루션을 찾았습니다.
따라서 전역 문서 수신기를 연결하지 않고 동일한 기능을 수행하려는 경우, 다음 예를 유효한 예로 고려할 수 있습니다.Safari 및 Firefox on OSX에서도 동작하지만 버튼 포커스이벤트 처리도 가능합니다.https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus
angular 8을 사용하는 스택비즈에서의 작업 예:
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();
}
}
저는 제 활용 사례를 바탕으로 저만의 솔루션을 올리기로 했습니다.Angular 11에서 (클릭) 이벤트가 있는 href가 있습니다.이렇게 하면 메인 앱의 메뉴 구성 요소가 꺼집니다.ts가 꺼집니다.
<li><a href="javascript:void(0)" id="menu-link" (click)="toggleMenu();" ><img id="menu-image" src="img/icons/menu-white.png" ></a></li>
메뉴 구성요소(예: div)는 "isMenuVisible"이라는 부울을 기반으로 표시됩니다(*ngIf).물론 드롭다운 또는 모든 구성 요소가 될 수 있습니다.
app.ts에는 이 간단한 기능이 있습니다.
@HostListener('document:click', ['$event'])
onClick(event: Event) {
const elementId = (event.target as Element).id;
if (elementId.includes("menu")) {
return;
}
this.isMenuVisble = false;
}
즉, "named" 콘텍스트 이외의 임의의 위치를 클릭하면 "named" 컴포넌트가 닫히거나 숨겨집니다.
이것은 컴포넌트 외부에 가까운 Angular Bootstrap DropDowns 버튼 샘플입니다.
쓸모없이bootstrap.js
// .html
<div class="mx-3 dropdown" [class.show]="isTestButton">
<button class="btn dropdown-toggle"
(click)="isTestButton = !isTestButton">
<span>Month</span>
</button>
<div class="dropdown-menu" [class.show]="isTestButton">
<button class="btn dropdown-item">Month</button>
<button class="btn dropdown-item">Week</button>
</div>
</div>
// .ts
import { Component, ElementRef, HostListener } from "@angular/core";
@Component({
selector: "app-test",
templateUrl: "./test.component.html",
styleUrls: ["./test.component.scss"]
})
export class TestComponent {
isTestButton = false;
constructor(private eleRef: ElementRef) {
}
@HostListener("document:click", ["$event"])
docEvent($e: MouseEvent) {
if (!this.isTestButton) {
return;
}
const paths: Array<HTMLElement> = $e["path"];
if (!paths.some(p => p === this.eleRef.nativeElement)) {
this.isTestButton = false;
}
}
}
엄청 복잡해.읽었지만 코드로 재현할 수 없습니다.자바 드롭다운 메뉴에 대한 코드를 가지고 있습니다.
document.addEventListener("mouseover", e => {
const isDropdownButton = e.target.matches("[data-dropdown-button]")
if (!isDropdownButton && e.closest('[data-dropdown]') != null) return
let currentDropDown
if (isDropdownButton) {
currentDropdown = e.target.closest('[data-dropdown]')
currentDropdown.classList.toggle('active')
}
document.querySelectorAll("[data-dropdown].active").forEach(dropdown => {
if (dropdown === currentDropdown) return
dropdown.classList.remove("active")
})
})
마우스 마우스를 움직이면 드롭다운이 열리고 열린 상태로 유지됩니다.하지만 두 가지 기능이 누락되어 있습니다.
- 다른 곳을 클릭해도 드롭다운이 닫히지 않습니다.
- 드롭다운 메뉴를 클릭하면 URL 주소로 이동합니다.감사해요.
저는 이와 유사한 문제에 대처하라는 지시를 내렸고 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>
문서에서 (클릭) 이벤트를 사용하여 이벤트 대상이 드롭다운 컨테이너 내에 없는지 확인할 수 있습니다.그렇지 않으면 드롭다운을 닫을 수 있습니다.다음은 예를 제시하겠습니다.
import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div #container>
<button (click)="toggleDropdown()">Toggle Dropdown</button>
<div *ngIf="isDropdownOpen">
Dropdown Content
</div>
</div>
`,
styles: []
})
export class AppComponent implements OnInit {
@ViewChild('container', { static: true }) container: ElementRef;
isDropdownOpen = false;
ngOnInit() {
document.addEventListener('click', (event) => {
if (!this.container.nativeElement.contains(event.target)) {
this.isDropdownOpen = false;
}
});
}
toggleDropdown() {
this.isDropdownOpen = !this.isDropdownOpen;
}
}
언급URL : https://stackoverflow.com/questions/35712379/how-can-i-close-a-dropdown-on-click-outside
'programing' 카테고리의 다른 글
기존 Eclipse 프로젝트와 기존 SVN 저장소 연결 (0) | 2023.04.24 |
---|---|
Azure 테이블: 파티션/행 키 선택 베스트 프랙티스 (0) | 2023.04.24 |
.gitignore 및 "다음 추적되지 않은 작업 트리 파일은 체크아웃에 의해 덮어쓰게 됩니다." (0) | 2023.04.24 |
Windows 배치 스크립트에서 "@"는 무엇을 의미합니까? (0) | 2023.04.24 |
__doPostBack() 사용방법 (0) | 2023.04.24 |