В vue 3 Compose API я показываю компонент всплывающего окна и хочу зафиксировать щелчок для закрытия модального окна.
мой код в родительском компоненте
<template>
<button @click = "toggleSettings" class = "bg-gray-300 text-gray-700 px-4 py-2 rounded flex items-center">
<span>⚙️</span>
</button>
<ModalSettings :visible = "showSettings" @onClose = "toggleSettings"/>
</template>
<script setup lang = "ts">
import { ref } from 'vue'
const showSettings = ref(false)
const toggleSettings = () => {
showSettings.value = !showSettings.value
}
</script>
Код моего дочернего компонента
<template>
<div v-if = "visible" class = "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div ref = "customDiv" @click.stop class = "bg-white p-8 rounded-lg shadow-lg w-96">
<h2 class = "text-xl font-bold mb-4"> Settings</h2>
</div>
</div>
</template>
<script setup lang = "ts">
import { ref, onMounted, onUnmounted, nextTick, defineProps } from 'vue'
const customDiv = ref<HTMLElement | null>(null)
const props = defineProps({
visible: Boolean
})
onMounted(() => {
nextTick(() => {
setTimeout(() => {
document.addEventListener('click', handleClickOutside)
}, 1000)
})
})
onUnmounted(() => {
document.removeEventListener("click", handleClickOutside)
})
const handleClickOutside = (event: MouseEvent) => {
if (customDiv.value && !customDiv.value.contains(event.target)) {
console.info('close the popup')
}
}
</script>
Проблема в том, что каждый раз, когда я показываю модальный компонент, это как если бы щелчок немедленно (событие с моим хаком тайм-аута) вызывался за пределами модального компонента и поэтому он закрывал компонент (здесь, в примере, он просто пишет «закройте неожиданно возникнуть")
Знаете ли вы, что происходит и как это предотвратить?
В противном случае, похоже, вот причина вашей проблемы: stackoverflow.com/a/35667286/8816585



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы монтируете компонент сразу вместе с кнопкой, чтобы ваш обработчик событий начал действовать немедленно. Щелчок кнопки распространяется на документ, а затем обрабатывается модальным окном.
Решением грубой силы было бы добавить к кнопке модификатор stop:
@click.stop = "toggleSettings"
В противном случае добавьте/удалите прослушиватель при изменении visible (нужно setTimeout пропустить нажатие кнопки и добавить прослушиватель после него):
Смотрите на Vue SFC Playground
watch(() => props.visible, value =>
setTimeout(() => document[`${value ? 'add' : 'remove'}EventListener`]('click', handleClickOutside)));
P.S. К сожалению, не удаляйте, обработчик также должен быть удален при размонтировании
onUnmounted(() => {
document.removeEventListener("click", handleClickOutside)
})
Вам не следует использовать прослушиватели DOM vanilla JS при использовании Vue, особенно если для такого варианта использования есть специальное решение: vueuse.org/core/onClickOutside Он также удалит хаки
timeOut+nextTickво что-то более чистое, IMO. .