removeEventListener未按预期工作

时间:2017-10-03 14:00:14

标签: javascript events vue.js vuejs2

我的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;
&#13;
&#13;

2 个答案:

答案 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);
}

...而不是将布尔值与truefalse===进行比较。你已经拥有一个布尔值,所以...我的意思是,你在哪里停止? if ((this.menuVisible) === true === true)? : - )

答案 1 :(得分:1)

这对我来说就像是一个X-Y问题。理想情况下,您不应该在JS(或VueJS甚至)中动态绑定/解除绑定事件侦听器。如果您只是想要以下行为:

  • 按钮单击切换菜单
  • 点击按钮外的应用关闭菜单

...那么你甚至不需要凌乱的事件处理。第一种行为可以通过简单的真/假开关来实现,即

this.menuVisible != this.menuVisible

请记住在按钮上的.stop事件活页夹上使用@click,这样您就可以阻止VueJS点击事件冒泡到应用程序。

同时,只需在this.menuVisible但在按钮外检测到点击事件时,只需将false设置为#app即可完成第二种行为。

&#13;
&#13;
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;
&#13;
&#13;