记录封装下拉选择组件点击外部DOM不能回收问题

1.在项目开发中,会选择一个组件库来提高我们的开发效率,在目前的项目中我们使用的是element-ui组件库,而element中的组件通常不能满足我们的业务需求,这时候我们就需要自己封装一个组件,这次遇到的一个需求如下图

这里点击版本和渲染问题,在网上搜寻一番尝试无果后,我选择自己封装一个下拉组件去完成

<template>
  <div class="aop_endcomps_comps_select">
    <div class="selectWrap">
      <div class="select-wrapper">
        <div class="select" @click="triggerOption">
          <div class="select-content" style="font-size: 14px">
            <span
              :class="{
                optionSpanActive:
                  (subject[0] && selectContent.status == '01') ||
                  selectContent.status == '',
                optionSpan: subject[0] && selectContent.status == '02',
              }"
              style="font-size: 12px"
              v-if="subject[0]"
              >{{ selectContent.status == "01" ? "已发布" : "待发布" }}</span
            >
            {{ selectContent.version ? selectContent.version : "无版本管理" }}
          </div>
          <div class="triangle-wrapper">
            <img src="~@m/assets/images/icon_下拉黑色.png" alt="" />
          </div>
        </div>
        <div class="option-wrapper" style="display: none" v-show="this.showSelect.a">
          <!-- 渲染父组件传来的值 -->
          <div
            class="option-item"
            v-for="(item, index) in list"
            :key="index"
            @click="choose(item)"
          >
            <div style="font-size: 14px">
              <span
                :class="{
                  optionSpanActive: item.status == '01' || item.status == '',
                  optionSpan: item.status == '02',
                }"
                style="font-size: 12px"
                >{{ item.status == "01" ? "已发布" : "待发布" }}</span
              >
              {{ item.version ? item.version : "" }}
            </div>
          </div>
          <div class="optionFooter" @click="$emit('toVersion')">
            组件库版本管理
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
  methods: {
    //深拷贝
    deepClone(obj) {
      let objClone = Array.isArray(obj) ? [] : {};
      if (obj && typeof obj === "object") {
        for (let key in obj) {
          if (obj[key] && typeof obj[key] === "object") {
            //判断对象的这条属性是否为对象
            objClone[key] = this.deepClone(obj[key]); //若是对象进行嵌套调用
          } else {
            objClone[key] = obj[key];
          }
        }
      }
      return objClone; //返回深度克隆后的对象
    },
    //处理数据函数
    getList(val) {
      this.arr = this.deepClone(val);
      if (this.arr.length == 1) {
        for (let i = 0; i < this.arr.length; i++) {
          this.list.push(this.arr[i]);
        }
      } else {
        for (let i = 0; i < this.arr.length; i++) {   
            this.list.push(this.arr[i]);
        }
      }
      // this.list = this.arr.splice(0,1);
      console.log(this.list);
    },
    triggerOption() {
      if (!this.subject[0]) {
        return;
      } else {
        if (this.optionWrapper.style.display == "none") {
          this.showSelect.a = true
          this.optionWrapper.style.display = "block";
        } else {
          this.showSelect.a = true
          this.optionWrapper.style.display = "none";
        }
      }
    },
    choose(item) {
      if (!this.subject[0]) {
        return;
      } else {
        if (this.arr.length == 1) {
          this.list = [];
          this.list = this.deepClone(this.arr);
        } else {
          // let idx = this.arr.findIndex((item1) => {
          //   return item.version === item1.version;
          // });
          // console.log(idx);
          this.list = [];
          this.list = this.deepClone(this.arr);
          // this.list.splice(idx, 1);
        }
        this.selectContent.version = item.version;
        this.selectContent.status = item.status;
        this.optionWrapper.style.display = "none";
        this.$emit("getReply", item);
      }
    },
  },

 组件封装完成,实现交互需求的时候发现两大问题:

第一个问题:需求是要求点击一个版本管理的时候显示当前点击的版本,然后下拉选择中的内容会剔除相应选择的一项,当时逻辑写出来以后发现自己选择之后会影响到父组件传来的值,并且选择之后非常混乱。

解决的方法是需要深拷贝传过来的数组,然后处理拷贝过后的数组,去遍历它渲染数据,而传过来的数组使用它当作模版。

第二个问题:需求是要求点击一个当前DOM的除了下拉选择组件的其他地方或者按esc键,下拉框会回收。

当时在网上搜了一种解决办法是这样的,在mounted里面给整体的页面文档绑定一个点击事件

然后用e.target判断是否点击的是组件内部的东西,如果不是传一个false的参数给组件,使组件的下拉框收回,但是这种情况就会有一个bug,就是当我点击菜单栏的其他按钮时会发生一些bug,这里bug没有记录,所以就不展示了,按esc键的时候也会有一些冲突。后面才知道可能是没有在destroyed里面删除这个事件,才触发的一系列bug。

还有一种解决方法,是在组件内部定义一个自定义方法如下:

  directives: {
    clickoutside: {
      bind(el, binding, vnode) {
        function documentHandler(e) {
          if (el.contains(e.target)) {
            return false;
          }
          if (binding.expression) {
            binding.value(e);
          }
        }
        function KeyUp(e) {
          if (e.keyCode == 27) {
            if (binding.expression) {
              binding.value(e);
            }
          }
        }
        el.__vueClickOutSize__ = documentHandler;
        el.__vueKeyup__ = KeyUp;
        document.addEventListener("keyup", KeyUp);
        document.addEventListener("click", documentHandler);
      },
      unbind(el, binding) {
        document.removeEventListener("click", el.__vueClickOutSize__);
        delete el.__vueClickOutSize__;
        document.removeEventListener("keyup", el.__vueKeyup__);
        delete el.__vueKeyup__;
      },
    },
  },

使用的是el.contain()这个函数,判断是否点击了当前使用这个指令的DOM和判断是否按下了ESC键,而bind.value()则是v-指令绑定的函数,判断条件来调用这个函数,而el.__vueClickOutside__ = documentHandler;这段代码是为了方便在unbind删除绑定的这个事件,当删除这个事件以后这个el.__vueClickOutside__属性就不需要了,所以使用delete去删除这个属性

然后再当前组件使用,就可以解决当前的需求问题了。 

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值