单击其他地方时触发组件方法

Posted

技术标签:

【中文标题】单击其他地方时触发组件方法【英文标题】:Trigger components methods when click elsewhere 【发布时间】:2017-11-20 03:40:40 【问题描述】:

我有一个 HeaderSubmenu 组件,如果单击按钮,它可以显示/隐藏下拉菜单。好的,但是现在我尝试找到一个好的解决方案,如果用户单击应用程序中的任何位置而不是此下拉菜单,它就会隐藏。

我将 Vue 2.3.3 与 Vuex 和 VueRouter 一起使用。

我的应用入口点:

'use strict';

import Vue from 'vue';
import VueRouter from 'vue-router';
import Vuex from 'vuex';

Vue.use(VueRouter);
Vue.use(Vuex);

import store_data from 'store';
import router from 'routes';

import App from 'app.vue';

var store = new Vuex.Store(store_data);

new Vue(
  el: '#app',
  store,
  router: router,
  render: function (createElement) 
    return createElement(App)
  
)

HeaderSubmenu 组件的模板:

<template>
  <li class="header-submenu">
    <!-- button to show/hide the drop-down menu -->
    <header-link v-bind="$props" :href="to ? false : '#'" @click.native="toggleMenu()">
      <slot name="item"></slot>
    </header-link>
    <!-- drop-down menu -->
    <ul class="submenu-list" :class="'open': open, 'animated': animated" @animationend="displaynone()">
      <slot></slot>
    </ul>
  </li>
</template>

这个组件在我的应用程序的某个地方,当用户点击&lt;ul class="submenu-list"&gt;以外的其他地方时,我想调用他的toggleMenu()方法。

我想到了一个全局事件总线,我的下拉菜单应该“注册”并在我的应用程序上检测全局 @click 事件。如果注册的菜单不是被点击的元素,我们会调用他的toggleMenu() 方法。 (理想情况下,我可以注册具有相同行为的其他元素。)

...但是我根本不掌握vue事件系统。 如何检查事件是否不在某个元素上? 我不知道如何实现这一目标。你能帮助我吗 ?谢谢!

====== 编辑 ======

在 Bert Evans 的帮助下,我使用了这个自定义指令:

// directive-clickoutside.js
export default 
  bind(el, binding, vnode) 
    el.event = 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.event)
  ,
  unbind(el) 
    document.body.removeEventListener('click', el.event)
  ,
;

// main.js
import clickout from 'utils/directive-clickoutside';
Vue.directive('clickout', clickout);

我在我的组件模板中使用了这个:

// HeaderSubmenu component
<template>
  <li class="header-submenu">
    <!-- élément du header servant à ouvrir le sous-menu -->
    <header-link v-bind="$props" :href="to ? false : '#'" @click.native="toggle()">
      <slot name="item"></slot>
    </header-link>
    <!-- sous-menu -->
    <ul class="submenu-list" :class="'open': open, 'animated': animated" @animationend="displaynone()" v-clickout="hide()">
      <slot></slot>
    </ul>
  </li>
</template>

但是当我点击页面上的任何地方时,这段代码会失败:

Uncaught TypeError: n.context[e.expression] is not a function
    at htmlBodyElement.t.event (directive-clickoutside.js:7)

问题出在哪里?

【问题讨论】:

Detect click outside element的可能重复 谢谢,我尝试使用自定义指令,但它以无限更新循环结束。我编辑了帖子以向您展示。 toggleMenu 是做什么的? toogleMenu 反转子菜单的显示状态(显示/隐藏)。这当然是无限循环的起源。当点击链接时我改为使用toogle(),当点击远离子菜单时,我改为使用hide()。但是自定义指令中出现了一个新问题(已编辑)。 【参考方案1】:

问题就在这里。

v-clickout="hide()"

实际上,您正在做的是将v-clickout 设置为hide() 的结果。相反,只需将 hide 方法传递给它。

v-clickout="hide"

一般来说,在你的模板中,如果你只是想让模板调用一个函数而不做任何特殊的事情,只需将函数的名称传递给它。

【讨论】:

哦,是的,所以它不像事件指令!我在点击事件上添加了stop 修饰符。谢谢 !我有最后一个问题:如果我有两个子菜单,当我打开第一个子菜单并打开另一个子菜单时,第一个子菜单不会关闭(当然是因为切换按钮的停止修饰符)。我怎样才能做到这一点? @Thaledric 首先,事件指令是相同的。您可以将它们设置为等于不带括号的名称。我很难想象第二种情况的情况。这些是真正的子菜单(菜单的子菜单)吗?也许是一个新问题,所以您可以提供所有详细信息。 谢谢!我在这里发布了一个新问题:***.com/questions/44613980/…【参考方案2】:

以下是创建适用于 Vue 3 的 click-outside 指令的方法:

app.directive('click-outside', 
  // bind
  beforeMount(el, binding) 
    el.clickOutsideEvent = function (event) 
      if (!(el == event.target || el.contains(event.target))) 
        binding.value(event)
      
    
    document.body.addEventListener('click', el.clickOutsideEvent)
  ,

  // unbind
  unmounted(el) 
    document.body.removeEventListener('click', el.clickOutsideEvent)
  ,
)

【讨论】:

以上是关于单击其他地方时触发组件方法的主要内容,如果未能解决你的问题,请参考以下文章

如何从其他组件触发反应组件

如何在Vue中触发点击事件[关闭]

为啥单击功能会在 Angular 2 中为自定义组件触发两次

如何在使用 Vue.js 触发任何事件时创建新组件?

在 React 中单击来自不同组件的按钮时触发另一个组件中的函数

如何从 ViewController 或其他地方调用或触发 AppDelegate 中的通知功能?