我的Vue应用程序中有一个相当简单的组件。当用户打开菜单时,我向#app
元素添加一个事件监听器以检测"外部"点击次数。
当用户点击外部时,它会关闭菜单并删除事件侦听器。这是按预期工作的。问题是,当用户单击按钮打开菜单,然后再次单击它以关闭它时,我 希望removeEventListener
因为菜单现已关闭。
然而这不起作用。当我通过按钮打开菜单然后通过按钮关闭它时,事件监听器仍保留在#app
元素上,即使我将其删除。
如果您多次单击该按钮,然后在该值显示true
时单击按钮外部,您将多次看到控制台日志outside click
。
new Vue({
el: "#app",
data: {
menuVisible: false
},
methods: {
click(e) {
var clickSource = e.target;
var app = document.getElementById("app");
var that = this;
function clickOutside(e) {
if (e.target != clickSource) {
console.log("outside click");
that.menuVisible = false;
app.removeEventListener("click", clickOutside);
}
}
if (this.menuVisible === false) {
this.menuVisible = true;
app.addEventListener("click", clickOutside);
} else if (this.menuVisible === true) {
this.menuVisible = false;
app.removeEventListener("click", clickOutside);
}
}
}
});

body {
margin: 0;
}
#app {
min-height: 100vh;
}

<script src="https://unpkg.com/vue"></script>
<div id="app">
{{menuVisible}}
<button @click="click">
click
</button>
</div>
&#13;
答案 0 :(得分:1)
问题在于,每个click
,您都会创建一个新 clickOutside
函数;如果您将该函数与removeEventListener
一起使用,因为没有事件注册 exact 函数对象,所以不会删除任何内容。
相反,请记住处理程序对象,以便您可以将其用于删除,请参阅注释:
new Vue({
el: "#app",
data: {
menuVisible: false,
clickOutside: null
},
methods: {
click(e) {
var clickSource = e.target;
var app = document.getElementById("app");
var that = this;
// Only create it if we don't already have it
if (!this.clickOutside) {
// Create it
this.clickOutside = function clickOutside(e) {
console.log("clickOutside triggered");
if (e.target != clickSource) {
console.log("It's an outside click");
that.menuVisible = false;
// Remove it
app.removeEventListener("click", that.clickOutside);
}
};
}
if (this.menuVisible === false) {
this.menuVisible = true;
app.addEventListener("click", this.clickOutside);
} else if (this.menuVisible === true) {
this.menuVisible = false;
app.removeEventListener("click", this.clickOutside);
}
}
}
});
body {
margin: 0;
}
#app {
min-height: 100vh;
background-color: #ddd;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
{{menuVisible}}
<button @click="click">
click
</button>
</div>
旁注:使用
更加惯用if (this.menuVisible) {
this.menuVisible = false;
app.removeEventListener("click", this.clickOutside);
} else {
this.menuVisible = true;
app.addEventListener("click", this.clickOutside);
}
甚至
this.menuVisible = !this.menuVisible;
if (this.menuVisible) {
app.addEventListener("click", this.clickOutside);
} else {
app.removeEventListener("click", this.clickOutside);
}
...而不是将布尔值与true
或false
与===
进行比较。你已经拥有一个布尔值,所以...我的意思是,你在哪里停止? if ((this.menuVisible) === true === true)
? : - )
答案 1 :(得分:1)
这对我来说就像是一个X-Y问题。理想情况下,您不应该在JS(或VueJS甚至)中动态绑定/解除绑定事件侦听器。如果您只是想要以下行为:
...那么你甚至不需要凌乱的事件处理。第一种行为可以通过简单的真/假开关来实现,即
this.menuVisible != this.menuVisible
请记住在按钮上的.stop
事件活页夹上使用@click
,这样您就可以阻止VueJS点击事件冒泡到应用程序。
同时,只需在this.menuVisible
但在按钮外检测到点击事件时,只需将false
设置为#app
即可完成第二种行为。
new Vue({
el: "#app",
data: {
menuVisible: false
},
methods: {
click(e) {
console.log('button clicked');
this.menuVisible = !this.menuVisible;
},
appClick(e) {
console.log('outside clicked');
this.menuVisible = false;
}
}
});
&#13;
body {
margin: 0;
}
#app {
min-height: 100vh;
}
&#13;
<script src="https://unpkg.com/vue"></script>
<div id="app" @click="appClick">
{{menuVisible}}
<button @click.stop="click">
click
</button>
</div>
&#13;