Vue使用directives自定义指令实现“点击div以外区域隐藏盒子内容“功能

官方文档介绍:自定义指令

主要就是利用vue指令的功能,获取所绑定元素的dom对象dom_A以及传递过来的回调方法fun_A,然后监听浏览器的mousedown和mouseup事件(mousedown作为辅助信息,真正触发传递的回调方法的是mouseup事件),当前事件中鼠标位置对应的dom对象dom_B不属于dom_A,则代表鼠标点击了dom_A外部,触发clickoutside回调方法。

参考:vue自定义指令clickoutside使用以及扩展用法

           element ui clickoutside 指令源码解读

1.引入指令

import Clickoutside from '@/utils/clickoutside'

新建一个clickoutside.js文件,自定义 clickOutside指令,当鼠标点击了指令所绑定的元素外面时触发事件:

import Vue from 'vue'

const isServer = Vue.prototype.$isServer

const on = (function () {
  if (!isServer && document.addEventListener) {
    return function (element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false)
      }
    }
  } else {
    return function (element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler)
      }
    }
  }
})()

const nodeList = []
const ctx = '@@clickoutsideContext'

let startClick
let seed = 0

!Vue.prototype.$isServer && on(document, 'mousedown', e => (startClick = e))

!Vue.prototype.$isServer &&
  on(document, 'mouseup', e => {
    nodeList.forEach(node => node[ctx].documentHandler(e, startClick))
  })

function createDocumentHandler(el, binding, vnode, excludes) {
  return function (mouseup = {}, mousedown = {}) {
    if (
      !vnode ||
      !vnode.context ||
      !mouseup.target ||
      !mousedown.target ||
      el.contains(mouseup.target) ||
      el.contains(mousedown.target) ||
      el === mouseup.target ||
      (vnode.context.popperElm &&
        (vnode.context.popperElm.contains(mouseup.target) ||
          vnode.context.popperElm.contains(mousedown.target)))
    ) {
      return
    }

    if (excludes && (excludes.includes(mousedown.target) || excludes.includes(mouseup.target))) {
      return
    }
    if (binding.expression && el[ctx].methodName && vnode.context[el[ctx].methodName]) {
      vnode.context[el[ctx].methodName]()
    } else {
      el[ctx].bindingFn && el[ctx].bindingFn()
    }
  }
}

/**
 * v-clickoutside
 * @desc 点击元素外面才会触发的事件
 * @example
 * ```vue
 * <div v-element-clickoutside="handleClose">
 * ```
 */
export default {
  bind(el, binding, vnode) {
    nodeList.push(el)
    const id = seed++
    el[ctx] = {
      id,
      documentHandler: createDocumentHandler(el, binding, vnode, binding.value.excludes),
      methodName: binding.expression,
      bindingFn: typeof binding.value === 'function' ? binding.value : binding.value.fn,
    }
  },

  update(el, binding, vnode) {
    el[ctx].documentHandler = createDocumentHandler(el, binding, vnode, binding.value.excludes)
    el[ctx].methodName = binding.expression

    el[ctx].bindingFn = typeof binding.value === 'function' ? binding.value : binding.value.fn
  },

  unbind(el) {
    const len = nodeList.length

    for (let i = 0; i < len; i++) {
      if (nodeList[i][ctx].id === el[ctx].id) {
        nodeList.splice(i, 1)
        break
      }
    }
    delete el[ctx]
  },
}

钩子函数 

————————————————————————————————————————

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数

————————————————————————————————————————

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。
    • arg:传给指令的参数,可选。
    • modifiers:一个包含修饰符的对象。
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

2.声明指令

export default {
  directives: { Clickoutside },
}

3.使用指令

<div
  v-if="activeRoom"
  v-show="showPop"
  v-clickoutside="e => popClickoutside('clickOutside')"
  @click.stop
>
<detailCard
  :room-info="activeRoom"
  :can-click-outside-close.sync="canClickOutsideClose"
  :show-pop.sync="showPop"
  :show-actions="showDetailCardActions"
  @refreshData="refreshData"
>
  <slot :room="activeRoom" />
</detailCard>

@click.stop 阻止事件冒泡

@click.prevent 阻止事件的默认行为

mounted() {
  window.addEventListener('scroll', this.popClickoutside)
},
beforeDestroy() {
  window.removeEventListener('scroll', this.popClickoutside)
},
methods: {
  popClickoutside(from) {
    if (from === 'clickOutside' && !this.canClickOutsideClose) {
      return
    }
    this.showPop = false
  },
  refreshData() {
    this.$emit('refreshData')
    this.showPop = false
  },
}

 windowScroll 监听页面滚动

我需要实现的效果是,页面中有几个大区域,大区域中排列着几个div小方块。点击某个大区域时,该区域内的小方块样式变大并且显示出序号,再点击其中某个大方块会弹出弹框显示大方块内具体情况。滑动页面或点击大区域外,弹框消失;再次点击该区域,大方块变小。

如果不添加scroll事件,如果点击了大区域中的某个div小方块,再点击大区域之外,关联的弹框内容还是存在并且会随着页面滚动。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值