【问题标题】:Vue.js custom directive: $refs is emptyVue.js 自定义指令:$refs 为空
【发布时间】:2020-03-31 09:27:37
【问题描述】:

背景

我按照以下指南创建了一个自定义指令(用于检测元素外部的点击):https://tahazsh.com/detect-outside-click-in-vue

元素:

<button
 id='burger'
 ref='burger'
 class='burger button is-white'>
 <span class='burger-top' aria-hidden='true' style='pointer-events: none;'></span>

 <span class='burger-bottom' aria-hidden='true' style='pointer-events: none;'></span>
</button>

指令:

import Vue from 'vue';

let handleOutsideClick;

Vue.directive('outside', {
  bind(el, binding, vnode) {
    setTimeout(() => {
      handleOutsideClick = (e) => {
        e.stopPropagation();

        const { handler, exclude } = binding.value;

        let clickedOnExcludedEl = false;

        exclude.forEach((refName) => {
          if (!clickedOnExcludedEl) {
            const excludedEl = vnode.context.$refs[refName]
            console.log(vnode.context.$refs);
            clickedOnExcludedEl = excludedEl.contains(e.target);
          }
        });

        if (!el.contains(e.target) && !clickedOnExcludedEl) {
          vnode.context[handler]();
        }
      };
      document.addEventListener('click', handleOutsideClick);
      document.addEventListener('touchstart', handleOutsideClick);
    }, 50);
  },
  unbind() {
    document.removeEventListener('click', handleOutsideClick);
    document.removeEventListener('touchstart', handleOutsideClick);
  },
});

请注意,我必须添加 setTimeout

在侧边栏组件中使用指令:

<div
  id='sidebar'
  class='sidebar-container'
  v-show='toggleSideBar'
  v-outside='{
    handler: "close",
    exclude: [
      "burger",
    ],
  }'>
  <div class='sidebar-content'>
    // other content
  </div>
</div>

问题

$refs 为空,即使该元素显然有一个 ref

console.log(vnode.context.$refs); 也清楚这一点。

值得注意的是,通过 id 获取元素是可行的:

        exclude.forEach((id) => {
          if (!clickedOnExcludedEl) {
            const excludedEl = document.getElementById(id);
            clickedOnExcludedEl = excludedEl.contains(e.target);
          }
        });

问题

为什么$refs元素明确存在且可以通过id访问时为空?

【问题讨论】:

  • 你有什么错误?
  • 您能否向我们展示采用此指令的完整标签标记?
  • 更新了我的帖子。汉堡在导航栏中。单击它将打开/关闭侧边栏。此处指令的要点是允许通过单击侧边栏外部来关闭侧边栏。

标签: vue.js vuejs2 vue-directives


【解决方案1】:

确实如此,因为您的ref 不是refName 的样子。

您的 ref 是“burger”,但 refName 是 exclude 赋予 v-closable 的属性,然后您可以对其进行重组。

在您链接到的教程中,refName button。所以我假设它在你的代码中是一样的。

确保ref="burger"

v-closable="{ exclude: ['burger'], // this is your refName handler: 'onClose' }"

都是一样的。

【讨论】:

  • 感谢您的回复。一切都是正确的,我已经更新了我的原始帖子以显示这一点。问题是$refs 是空的(即使它不应该是),所以.contains 失败。
  • 尝试将外部单引号 ' 切换为双引号 ",然后将内部双引号切换为单引号。
【解决方案2】:

此代码当前的编写方式ref='burger'v-outside 必须在同一个模板中。

关键是:

const excludedEl = vnode.context.$refs[refName]

vnode.context 将引用侧边栏组件的 Vue 实例。 $refs 必须在该组件的模板中定义。

$refs 对于特定的 Vue 实例是本地的。 “Vue 实例”是指组件实例,如果该术语更清楚的话。所以侧边栏将有一组$refs,而导航栏将有自己的一组$refs

根据问题下的评论,似乎该按钮当前是在导航栏组件的模板中定义的。所以你最终会在导航栏的$refs 中得到一个burger ref,但该指令正在侧边栏的$refs 中寻找它。

指令的编写方式需要一个有点像这样的模板:

<div>
  <div
    v-outside='{
      handler: "close",
      exclude: ["burger"]
    }'
  >
    ...  
  </div>
  <button ref='burger'>...</button>
</div>

这里的v-outsideref='burger' 都在同一个模板中,所以一切正常。一旦您将按钮移动到其他模板,它将无法找到它。

使用iddocument.getElementById 是一个非常不同的场景。元素 ID 是全局注册的。元素在 DOM 中的位置无关紧要,只要它存在就会被找到。

【讨论】:

  • 感谢您的解释。这就说得通了。我想在这种情况下我会继续使用 id。
猜你喜欢
  • 2019-02-23
  • 2019-01-05
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
相关资源
最近更新 更多