当前位置:首页 > 科技  > 软件

Vue 点击弹窗外部,实现弹窗关闭?你有实现的思路吗?

来源: 责编: 时间:2024-06-25 17:13:21 90观看
导读背景记得以前面试官问过我一个问题:我现在有一个弹窗,怎样才能实现点击弹窗以外的区域,实现关闭弹窗呢?当时确实比较菜,没想出应该怎么做才行,因为当时我的脑子里只有 click事件,我在想点击事件不是只能绑定在本元素身上吗?怎

背景

记得以前面试官问过我一个问题:我现在有一个弹窗,怎样才能实现点击弹窗以外的区域,实现关闭弹窗呢?0ai28资讯网——每日最新资讯28at.com

当时确实比较菜,没想出应该怎么做才行,因为当时我的脑子里只有 click事件,我在想点击事件不是只能绑定在本元素身上吗?怎么才能点击其他地方来影响本元素呢?0ai28资讯网——每日最新资讯28at.com

0ai28资讯网——每日最新资讯28at.com

实现思路

过了一两年后,回头发现,其实实现并不困难,很多人其实也都会,换一种说法,面试官想问的是:在 Vue 中,有一个元素X,怎么做到点击元素X以外的东西,触发绑定在元素X上的事件。0ai28资讯网——每日最新资讯28at.com

我把实现思路分为几步:0ai28资讯网——每日最新资讯28at.com

  • 定义一个 Map,来收集弹窗元素
  • 监听 document 的鼠标按下、松开事件
  • document 鼠标按下时记录触发的元素A
  • document 鼠标松开时遍历 Map 中所有弹窗元素,让这些弹窗元素跟元素A一一比较,不等于则说明是点了弹窗元素外部,等于则说明点击了弹窗元素内部
  • 点击了外部则触发绑定事件,点击了内部则不触发

ClickOutside

其实上面的思路,就是 v-clickoutside的实现思路,这个自定义指令,是 Vue 中用的非常广泛的指令~具体用法是这样的:0ai28资讯网——每日最新资讯28at.com

cs () {  console.log('点击外部')}<div v-clickoutside="cs"></div><button>点我</button><button>哈哈哈</button>

当你点击了 div 元素,也就是本元素,并不会触发事件 cs,而当你点击它以外的元素,则会触发 cs 事件。0ai28资讯网——每日最新资讯28at.com

代码实现

1.TypeScript类型准备

// vue自带的一些类型import type { ComponentPublicInstance, DirectiveBinding, ObjectDirective } from 'vue';// 下面会用到,是记录绑定事件的函数type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;// Map 的类型// key 是元素本地// value 是绑定的事件type FlushList = Map<  HTMLElement,  DocumentHandler>;

2.绑定事件函数

首先封装一个绑定事件的函数,大家在平时封装函数的时候一定要注意判空,兜底~0ai28资讯网——每日最新资讯28at.com

export function on(  element: Element | HTMLElement | Document | Window,  event: string,  handler: EventListenerOrEventListenerObject,): void {  if (element && event && handler) {    element.addEventListener(event, handler, false);  }}

3.判断点击元素是否是本元素

想一想我们的目的是啥,有一元素A,我需要点击元素A以外的地方才触发绑定的事件,点击元素A或者元素A以内的区域则不触发0ai28资讯网——每日最新资讯28at.com

所以这个函数主要做几件事:0ai28资讯网——每日最新资讯28at.com

  • 判断点击的元素是否是本元素(不触发)
  • 判断点击的元素是否在本元素内(不触发)
  • 兜底,判断元素是否存在(触发)
function createDocumentHandler(el: HTMLElement, binding: DirectiveBinding): DocumentHandler {  return function (mouseup, mousedown) {    const mouseUpTarget = mouseup.target as Node;    const mouseDownTarget = mousedown.target as Node;    const isBound = !binding || !binding.instance;    const isTargetExists = !mouseUpTarget || !mouseDownTarget;    const isContainedByEl = el.contains(mouseUpTarget) || el.contains(mouseDownTarget);    const isSelf = el === mouseUpTarget;    if (      isBound ||      isTargetExists ||      isContainedByEl ||      isSelf    ) {      return;    }    binding.value();  };}

4.自定义指令

自定义指令的几个生命周期里,需要做这些事:0ai28资讯网——每日最新资讯28at.com

  • 绑定时,记录绑定元素与绑定事件到 nodeList 中
  • 更新时,记录绑定元素与绑定事件到 nodeList 中
  • 销毁时,将此元素从 nodeList 中抹除
// 记录绑定元素的 Mapconst nodeList: FlushList = new Map();const ClickOutside: ObjectDirective = {  beforeMount(el, binding) {    nodeList.set(el,       createDocumentHandler(el, binding));  },  updated(el, binding) {    nodeList.set(el,       createDocumentHandler(el, binding));  },  unmounted(el) {    nodeList.delete(el);  },};export default ClickOutside;

5.监听 document 鼠标按下、松开

万事俱备只欠东风,现在只需要监听 document 的鼠标按下、松开事件 即可,大概分为几步:;0ai28资讯网——每日最新资讯28at.com

  • 鼠标按下时,记录这个触发的元素
  • 鼠标松开时,遍历 nodeList 中的元素,跟这个触发元素做对比
  • 符合条件则执行绑定事件,不符合则不执行
let startClick: MouseEvent;on(document, 'mousedown', (e: MouseEvent) => (startClick = e));on(document, 'mouseup', (e: MouseEvent) => {  for (const { documentHandler } of nodeList.values()) {    documentHandler(e, startClick);  }});

这就实现了点击外部触发内部事件的效果了!0ai28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-96418-0.htmlVue 点击弹窗外部,实现弹窗关闭?你有实现的思路吗?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 宁德时代曾毓群回应“奋斗一百天”:号召练好基本功,没有强迫大家

下一篇: Java 流式编程的七个必学技巧

标签:
  • 热门焦点
Top
Baidu
map