요소 외부 클릭 감지
요소 외부에서 클릭을 감지하려면 어떻게해야합니까? Vue.js를 사용하고 있으므로 템플릿 요소 외부에 있습니다. Vanilla JS에서 수행하는 방법을 알고 있지만 Vue.js를 사용할 때 더 적절한 방법이 있는지 잘 모르겠습니다.
이것은 Vanilla JS의 솔루션 입니다. div 외부의 Javascript Detect Click 이벤트
요소에 액세스하는 더 좋은 방법을 사용할 수 있을까요?
사용자 지정 지시문을 한 번 설정하면 멋지게 해결할 수 있습니다.
Vue.directive('click-outside', {
bind () {
this.event = event => this.vm.$emit(this.expression, event)
this.el.addEventListener('click', this.stopProp)
document.body.addEventListener('click', this.event)
},
unbind() {
this.el.removeEventListener('click', this.stopProp)
document.body.removeEventListener('click', this.event)
},
stopProp(event) { event.stopPropagation() }
})
용법:
<div v-click-outside="nameOfCustomEventToCall">
Some content
</div>
구성 요소에서 :
events: {
nameOfCustomEventToCall: function (event) {
// do something - probably hide the dropdown menu / modal etc.
}
}
경고에 대한 추가 정보가 포함 된 JSFiddle의 작업 데모 :
https://jsfiddle.net/Linusborg/yzm8t8jq/
Linus Borg 답변을 기반으로하고 vue.js 2.0에서 잘 작동하는 솔루션이 있습니다.
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
// here I check that click was outside the el and his childrens
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call method provided in attribute value
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
});
작은 데모가 있습니다
https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments 에서 사용자 지정 지시문과 el, binding, vnode의 의미에 대한 자세한 정보를 찾을 수 있습니다.
이 작업을 위해 커뮤니티에서 두 가지 패키지를 사용할 수 있습니다 (둘 다 유지 관리 됨).
tabindex
집중할 수 있도록 구성 요소에 속성을 추가 하고 다음을 수행합니다.
<template>
<div
@focus="handleFocus"
@focusout="handleFocusOut"
tabindex="0"
>
SOME CONTENT HERE
</div>
</template>
<script>
export default {
methods: {
handleFocus() {
// do something here
},
handleFocusOut() {
// do something here
}
}
}
</script>
export default {
bind: function (el, binding, vNode) {
// Provided expression must evaluate to a function.
if (typeof binding.value !== 'function') {
const compName = vNode.context.name
let warn = `[Vue-click-outside:] provided expression '${binding.expression}' is not a function, but has to be`
if (compName) { warn += `Found in component '${compName}'` }
console.warn(warn)
}
// Define Handler and cache it on the element
const bubble = binding.modifiers.bubble
const handler = (e) => {
if (bubble || (!el.contains(e.target) && el !== e.target)) {
binding.value(e)
}
}
el.__vueClickOutside__ = handler
// add Event Listeners
document.addEventListener('click', handler)
},
unbind: function (el, binding) {
// Remove Event Listeners
document.removeEventListener('click', el.__vueClickOutside__)
el.__vueClickOutside__ = null
}
}
이것은 Vue.js 2.5.2에서 나를 위해 일했습니다.
/**
* Call a function when a click is detected outside of the
* current DOM node ( AND its children )
*
* Example :
*
* <template>
* <div v-click-outside="onClickOutside">Hello</div>
* </template>
*
* <script>
* import clickOutside from '../../../../directives/clickOutside'
* export default {
* directives: {
* clickOutside
* },
* data () {
* return {
showDatePicker: false
* }
* },
* methods: {
* onClickOutside (event) {
* this.showDatePicker = false
* }
* }
* }
* </script>
*/
export default {
bind: function (el, binding, vNode) {
el.__vueClickOutside__ = event => {
if (!el.contains(event.target)) {
// call method provided in v-click-outside value
vNode.context[binding.expression](event)
event.stopPropagation()
}
}
document.body.addEventListener('click', el.__vueClickOutside__)
},
unbind: function (el, binding, vNode) {
// Remove Event Listeners
document.removeEventListener('click', el.__vueClickOutside__)
el.__vueClickOutside__ = null
}
}
나는 모든 답변 (vue-clickaway의 라인 포함)을 결합하고 나를 위해 작동하는이 솔루션을 생각해 냈습니다.
Vue.directive('click-outside', {
bind(el, binding, vnode) {
var vm = vnode.context;
var callback = binding.value;
el.clickOutsideEvent = function (event) {
if (!(el == event.target || el.contains(event.target))) {
return callback.call(vm, event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent);
},
unbind(el) {
document.body.removeEventListener('click', el.clickOutsideEvent);
}
});
구성 요소에서 사용 :
<li v-click-outside="closeSearch">
<!-- your component here -->
</li>
이 코드를 사용합니다.
숨기기 표시 버튼
<a @click.stop="visualSwitch()"> show hide </a>
표시 숨기기 요소
<div class="dialog-popup" v-if="visualState" @click.stop=""></div>
스크립트
data () { return {
visualState: false,
}},
methods: {
visualSwitch() {
this.visualState = !this.visualState;
if (this.visualState)
document.addEventListener('click', this.visualState);
else
document.removeEventListener('click', this.visualState);
},
},
업데이트 : 시계 제거; 전파 중지 추가
다음과 같이 클릭 이벤트에 대해 두 개의 이벤트 리스너를 등록 할 수 있습니다.
document.getElementById("some-area")
.addEventListener("click", function(e){
alert("You clicked on the area!");
e.stopPropagation();// this will stop propagation of this event to upper level
}
);
document.body.addEventListener("click",
function(e) {
alert("You clicked outside the area!");
}
);
Mobile Safari를 지원하기 위해 MadisonTrash의 답변을 업데이트했습니다 ( click
이벤트 가 없으므로 touchend
대신 사용해야 함). 또한 모바일 장치에서 드래그하여 이벤트가 트리거되지 않도록하는 검사도 포함됩니다.
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.eventSetDrag = function () {
el.setAttribute('data-dragging', 'yes');
}
el.eventClearDrag = function () {
el.removeAttribute('data-dragging');
}
el.eventOnClick = function (event) {
var dragging = el.getAttribute('data-dragging');
// Check that the click was outside the el and its children, and wasn't a drag
if (!(el == event.target || el.contains(event.target)) && !dragging) {
// call method provided in attribute value
vnode.context[binding.expression](event);
}
};
document.addEventListener('touchstart', el.eventClearDrag);
document.addEventListener('touchmove', el.eventSetDrag);
document.addEventListener('click', el.eventOnClick);
document.addEventListener('touchend', el.eventOnClick);
}, unbind: function (el) {
document.removeEventListener('touchstart', el.eventClearDrag);
document.removeEventListener('touchmove', el.eventSetDrag);
document.removeEventListener('click', el.eventOnClick);
document.removeEventListener('touchend', el.eventOnClick);
el.removeAttribute('data-dragging');
},
});
누군가 모달 외부를 클릭 할 때 모달을 숨기는 방법을 찾고 있다면. 모달에는 일반적으로 클래스 modal-wrap
또는 이름 @click="closeModal"
을 지정한 래퍼가 있으므로 래퍼에 넣을 수 있습니다 . vuejs 문서에 명시된 이벤트 처리를 사용 하여 클릭 한 대상이 래퍼 또는 모달에 있는지 확인할 수 있습니다.
methods: {
closeModal(e) {
this.event = function(event) {
if (event.target.className == 'modal-wrap') {
// close modal here
this.$store.commit("catalog/hideModal");
document.body.removeEventListener("click", this.event);
}
}.bind(this);
document.body.addEventListener("click", this.event);
},
}
<div class="modal-wrap" @click="closeModal">
<div class="modal">
...
</div>
<div>
@Denis Danilenko 솔루션이 저에게 효과적입니다. 여기 제가 한 일이 있습니다. 그런데 여기서 VueJS CLI3 및 NuxtJS를 Bootstrap4와 함께 사용하고 있지만 NuxtJS없이 VueJS에서도 작동합니다.
<div
class="dropdown ml-auto"
:class="showDropdown ? null : 'show'">
<a
href="#"
class="nav-link"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
@click="showDropdown = !showDropdown"
@blur="unfocused">
<i class="fas fa-bars"></i>
</a>
<div
class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownMenuLink"
:class="showDropdown ? null : 'show'">
<nuxt-link class="dropdown-item" to="/contact">Contact</nuxt-link>
<nuxt-link class="dropdown-item" to="/faq">FAQ</nuxt-link>
</div>
</div>
export default {
data() {
return {
showDropdown: true
}
},
methods: {
unfocused() {
this.showDropdown = !this.showDropdown;
}
}
}
지시문에서 사용자 정의 네이티브 자바 스크립트 이벤트를 생성 할 수 있습니다. node.dispatchEvent를 사용하여 노드에서 이벤트를 전달하는 지시문을 만듭니다.
let handleOutsideClick;
Vue.directive('out-click', {
bind (el, binding, vnode) {
handleOutsideClick = (e) => {
e.stopPropagation()
const handler = binding.value
if (el.contains(e.target)) {
el.dispatchEvent(new Event('out-click')) <-- HERE
}
}
document.addEventListener('click', handleOutsideClick)
document.addEventListener('touchstart', handleOutsideClick)
},
unbind () {
document.removeEventListener('click', handleOutsideClick)
document.removeEventListener('touchstart', handleOutsideClick)
}
})
이렇게 사용할 수있는
h3( v-out-click @click="$emit('show')" @out-click="$emit('hide')" )
다음과 같이 본문 끝에 div를 만듭니다.
<div v-if="isPopup" class="outside" v-on:click="away()"></div>
.outside는 다음과 같습니다.
.outside {
width: 100vw;
height: 100vh;
position: fixed;
top: 0px;
left: 0px;
}
그리고 away ()는 Vue 인스턴스의 메소드입니다.
away() {
this.isPopup = false;
}
쉽고 잘 작동합니다.
루트 요소 내부에 여러 요소가있는 구성 요소가있는 경우 부울과 함께이 It just works ™ 솔루션을 사용할 수 있습니다 .
<template>
<div @click="clickInside"></div>
<template>
<script>
export default {
name: "MyComponent",
methods: {
clickInside() {
this.inside = true;
setTimeout(() => (this.inside = false), 0);
},
clickOutside() {
if (this.inside) return;
// handle outside state from here
}
},
created() {
this.__handlerRef__ = this.clickOutside.bind(this);
document.body.addEventListener("click", this.__handlerRef__);
},
destroyed() {
document.body.removeEventListener("click", this.__handlerRef__);
},
};
</script>
이 패키지 사용 vue-click-outside
간단하고 신뢰할 수 있으며 현재 다른 많은 패키지에서 사용됩니다. 필수 구성 요소에서만 패키지를 호출하여 자바 스크립트 번들 크기를 줄일 수도 있습니다 (아래 예 참조).
npm install vue-click-outside
사용법 :
<template>
<div>
<div v-click-outside="hide" @click="toggle">Toggle</div>
<div v-show="opened">Popup item</div>
</div>
</template>
<script>
import ClickOutside from 'vue-click-outside'
export default {
data () {
return {
opened: false
}
},
methods: {
toggle () {
this.opened = true
},
hide () {
this.opened = false
}
},
mounted () {
// prevent click outside event with popupItem.
this.popupItem = this.$el
},
// do not forget this section
directives: {
ClickOutside
}
}
</script>
<button
class="dropdown"
@click.prevent="toggle"
ref="toggle"
:class="{'is-active': isActiveEl}"
>
Click me
</button>
data() {
return {
isActiveEl: false
}
},
created() {
window.addEventListener('click', this.close);
},
beforeDestroy() {
window.removeEventListener('click', this.close);
},
methods: {
toggle: function() {
this.isActiveEl = !this.isActiveEl;
},
close(e) {
if (!this.$refs.toggle.contains(e.target)) {
this.isActiveEl = false;
}
},
},
이 질문에 대한 답변은 이미 많으며 대부분은 유사한 사용자 지정 지침 아이디어를 기반으로합니다. 이 접근 방식의 문제점은 메소드 함수를 지시문에 전달해야하고 다른 이벤트 에서처럼 코드를 직접 작성할 수 없다는 것입니다.
vue-on-clickout
다른 새 패키지 를 만들었습니다 . 다음에서 확인하세요.
v-on:clickout
다른 이벤트처럼 쓸 수 있습니다. 예를 들어 다음과 같이 작성할 수 있습니다.
<div v-on:clickout="myField=value" v-on:click="myField=otherValue">...</div>
그리고 그것은 작동합니다.
나는 추가 기능이 싫어서 ... 추가 vue 메소드가없는 멋진 vue 솔루션이 있습니다.
- HTML 요소 생성, 컨트롤 및 지시문 설정
<p @click="popup = !popup" v-out="popup">
<div v-if="popup">
My awesome popup
</div>
- 다음과 같은 데이터에 var 만들기
data:{
popup: false,
}
- vue 지시문을 추가하십시오. 이것의
Vue.directive('out', {
bind: function (el, binding, vNode) {
const handler = (e) => {
if (!el.contains(e.target) && el !== e.target) {
//and here is you toggle var. thats it
vNode.context[binding.expression] = false
}
}
el.out = handler
document.addEventListener('click', handler)
},
unbind: function (el, binding) {
document.removeEventListener('click', el.out)
el.out = null
}
})
자주 사람들은 사용자가 루트 구성 요소를 떠 났는지 알고 싶어합니다 (모든 수준 구성 요소에서 작동).
Vue({
data: {},
methods: {
unfocused : function() {
alert('good bye');
}
}
})
<template>
<div tabindex="1" @blur="unfocused">Content inside</div>
</template>
토글 드롭 다운 메뉴를 처리하는 솔루션이 있습니다.
export default {
data() {
return {
dropdownOpen: false,
}
},
methods: {
showDropdown() {
console.log('clicked...')
this.dropdownOpen = !this.dropdownOpen
// this will control show or hide the menu
$(document).one('click.status', (e)=> {
this.dropdownOpen = false
})
},
}
I am using this package : https://www.npmjs.com/package/vue-click-outside
It works fine for me
HTML :
<div class="__card-content" v-click-outside="hide" v-if="cardContentVisible">
<div class="card-header">
<input class="subject-input" placeholder="Subject" name=""/>
</div>
<div class="card-body">
<textarea class="conversation-textarea" placeholder="Start a conversation"></textarea>
</div>
</div>
My script codes :
import ClickOutside from 'vue-click-outside'
export default
{
data(){
return {
cardContentVisible:false
}
},
created()
{
},
methods:
{
openCardContent()
{
this.cardContentVisible = true;
}, hide () {
this.cardContentVisible = false
}
},
directives: {
ClickOutside
}
}
참고URL : https://stackoverflow.com/questions/36170425/detect-click-outside-element
'Development Tip' 카테고리의 다른 글
요소 링크에 필수 속성 속성이 없습니다. (0) | 2020.10.08 |
---|---|
Windows에 Ruby Gem 설치 (0) | 2020.10.08 |
Ansible로 여러 파일 복사 (0) | 2020.10.08 |
순수 기능의 이점 (0) | 2020.10.08 |
R을 사용하여 생일 음악을 재생하려면 어떻게해야합니까? (0) | 2020.10.08 |