首页 > 解决方案 > Vue中如何实现两个任意元素之间的通信?

问题描述

我目前正在使用 Vue 框架构建一个应用程序,遇到了一个奇怪的问题,到目前为止我无法找到一个很好的解决方案:

我要做的是向父容器添加一个类,以防容器内的特定元素(输入、选择、文本区域等)获得焦点。这是示例代码:

  <div class="form-group placeholder-label">
    <label for="desc"><span>Description</span></label>
    <div class="input">
      <input id="desc" type="text" />
    </div>
  </div>

当然,在 Vanilla JS 中,这很容易做到:

const parent = document.querySelector('.placeholder-label');
const input = parent.querySelector('input');
input.addEventListener('focus', (e) => {
  parent.classList.add('active');
});

同样,您可以遍历所有.placeholder-label元素并将事件添加到它们的子输入/选择等以添加此基本功能。这里有两个活动部分:

  1. 你不知道父元素的类型,只知道它上面有.placeholder-label
  2. 您不知道子元素的类型,只知道它是父元素内的某种 HTML 表单元素。

我可以构建一个 Vue 组件,根据给定子元素的焦点/模糊来切换给定父元素上的类吗?我能想到的最好的办法是为子元素使用插槽,但是我仍然需要为每个父元素构建一个组件。即使将 mixins 用于重用部分,与我需要用纯 JS 编写的五行代码相比,它仍然是一团糟。

我的模板:

<template>
  <div
    class="form-group"
    :class="{ 'active': active }"
  >
    <label :for="inputID"><span>{{ inputLabel }}</span></label>
    <slot
      name="input"
      :focusFunc="makeActive"
      :blurFunc="makeInactive"
      :inputLabel="inputLabel"
      :inputID="inputID"
    />
  </div>
</template>

<script>
export default {
  name: 'TestInput',
  props: {
    inputLabel: {
      type: String,
      default: '',
    },
    inputID: {
      type: String,
      required: true,
    },
  },
  // data could be put into a mixin
  data() {
    return {
      active: false,
    };
  },
  // methods could be put into a mixin
  methods: {
    makeActive() {
      this.active = true;
    },
    makeInactive() {
      this.active = false;
    },
  },
};
</script>

用法:

<test-input
  :input-i-d="'input-2'"
  :input-label="'Description'"
>
  <template v-slot:input="scopeVars">
    <!-- this is a bootstrap vue input component -->
    <b-form-input
      :id="scopeVars.inputID"
      :state="false"
      :placeholder="scopeVars.inputLabel"
      @blur="scopeVars.blurFunc"
      @focus="scopeVars.focusFunc"
    />
  </template>
</test-input>

我想我只是遗漏了一些东西,或者这是 Vue 无法优雅解决的问题?

编辑:如果您正在寻找一种处理气泡事件的方法,请点击此处。但是,我认为这不适用于插槽,这对于解决我的组件问题是必要的。

标签: javascriptvue.jsvuejs2vue-component

解决方案


对于那些想知道这里有两个解决方案的人。似乎我确实在插槽和所有方面都过度考虑了这个问题。最初,我觉得为给定元素构建一个基于给定子元素焦点接收类的组件有点太多了。事实证明确实如此,您可以在模板或 css 中轻松解决此问题。

  1. CSS:感谢@Davide Castellini 提出 :focus-within 伪选择器。我以前没听说过那个。它适用于较新的浏览器,并且有一个可用的 polyfill
  2. 模板我写了一个小的自定义指令,可以应用于子元素并处理所有事情。

用法:

v-toggle-parent-class="{ selector: '.placeholder-label', className: 'active' }"

指示:

directives: {
  toggleParentClass: {
    inserted(el, { value }) {
      const parent = el.closest(value.selector);
      if (parent !== null) {
        el.addEventListener('focus', () => {
          parent.classList.add(value.className);
        });
        el.addEventListener('blur', () => {
          parent.classList.remove(value.className);
        });
      }
    },
  },
},

推荐阅读