点击弹框外关闭弹框

  • 核心是监听全局点击事件,通过判断点击时的dom元素是否包含在弹框的dom中,即弹框dom.contains(event.target)
<body><div class="modal">Modal</div><script>const model = document.querySelector('.modal');function toggle(open) {model.style.display = open ? 'block' : 'none';}window.addEventListener('click', (event) => {if (!model.contains(event.target)) {toggle(false);}});</script>
</body>

特殊情况:当点击iframe时

  • 用户在点击 iframe 时在 window 上无法监听到 click 事件的
  • 如果 iframe 中页面没跨域则可以使用 iframe.contentWindow.addEventListener 来监听
  • 跨域的页面如果可控则可以通过 postMessage 通知父级窗口

通过焦点管理来代替点击事件

  • 弹窗内部与外部都有 input 元素,此时可使用键盘的 Tab 来切换选中 input,当输入框从弹窗内部切换到外部时,弹窗关闭是一个比较正常的行为,使用 click 事件是无法处理键盘 Tab 的切换焦点元素的
    <div class="modal"><input type="text" /></div><input type="text" />

解决方案焦点管理:

  • 直接监听 focus 与 blur 事件是不生效的,因为普通元素并不支持焦点事件,让元素支持焦点事件需要为元素设置 tabindex 属性
  <div class="modal" tabindex="0">model</div><input type="text" /><iframe></iframe><script src="./1.js"></script><script>const model = document.querySelector('.modal')//元素可以点击获取焦点,但不能通过tab键model.setAttribute('tabindex', '-1');function toggle(open) {model.style.display = open ? 'block' : 'none';}function onFocus(event) {console.log('focus')}function onBlur(event) {console.log('blur')toggle(false);}model.addEventListener('focus', onFocus);model.addEventListener('blur', onBlur);
  • 此时可以通过焦点管理来实现关闭弹框功能

  • 但存在当弹框内部出现输入框时,会因为点击输入框失去弹框容器的焦点,导致弹框关闭

    • 原因是focus和blur是不支持冒泡的,内部子元素的focus并不会往上传递到弹框

    • 通过开启捕获事件优先触发外层焦点或focusin|focusout支持冒泡代替

  • 必须使用异步任务来进行关闭弹框,如果同步关闭后,会失去元素,无法再触发弹框的focus进行恢复

<body><div class="modal" tabindex="0">model<input type="text" /></div><input type="text" /><iframe></iframe><script src="./1.js"></script><script>const model = document.querySelector('.modal')model.setAttribute('tabindex', '-1');function toggle(open) {model.style.display = open ? 'block' : 'none';}let blurTimer = null;function onFocus(event) {console.log('focus')//恢复弹框clearTimeout(blurTimer);}function onBlur(event) {console.log('blur')blurTimer=setTimeout(()=>{ toggle(false);})}model.addEventListener('focus', onFocus,true);model.addEventListener('blur', onBlur,true);// model.addEventListener('focusin', onFocus);// model.addEventListener('focusout', onBlur);</script>
</body>
  • 此时解决了弹框内部输入框冒泡等问题
  • 如果弹框内部有<input type=“file” />或iframe,这两者最终都只会触发弹框blur事件使得弹框关闭,此时需要通过document.activeElement即当前获得焦点的元素结合全局焦点事件进行判断
  • 对于弹框内部有<input type=“file” />,此时监听全局blur事件,触发时判断document.activeElement(此时为<input type=“file” />)是否在model内,是的话取消掉弹框blur中的关闭,当文件选择后,焦点会重新放在input上,此时会根据之前设置的冒泡等原因使得弹框能重新获取焦点
<body><div class="modal" tabindex="0">model<input type="text" /><input type="file" /><iframe width="300" height="200"></iframe></div><input type="text" /><iframe></iframe><script src="./1.js"></script><script>const model = document.querySelector('.modal')model.setAttribute('tabindex', '-1');function toggle(open) {model.style.display = open ? 'block' : 'none';}let blurTimer = null;function onFocus(event) {console.log('focus')console.log(document.activeElement)//恢复弹框clearTimeout(blurTimer);}function onBlur(event) {console.log('blur')blurTimer=setTimeout(()=>{ toggle(false);})}model.addEventListener('focus', onFocus,true);model.addEventListener('blur', onBlur,true);// model.addEventListener('focusin', onFocus);// model.addEventListener('focusout', onBlur);window.addEventListener('blur', () => {console.log('window blur')console.log(document.activeElement)if (model.contains(document.activeElement)) {//清除异步函数,取消弹框关闭onFocus();// model.focus()}});</script>
</body>
  • 当弹框内有iframe时,虽然在上一部阻止了弹框关闭,但iframe的焦点并不会往上触发弹框的焦点,所以会造成弹框开启但弹框无焦点,导致后续无法关闭的问题

    • 解决方式一:在全局blur中阻止弹框关闭后,再调用弹框.focus()手动使弹框获取焦点
  <script>const model = document.querySelector('.modal')model.setAttribute('tabindex', '-1');function toggle(open) {model.style.display = open ? 'block' : 'none';}let blurTimer = null;function onFocus(event) {console.log('focus')console.log(document.activeElement)//恢复弹框clearTimeout(blurTimer);}function onBlur(event) {console.log('blur')blurTimer=setTimeout(()=>{ toggle(false);})}model.addEventListener('focus', onFocus,true);model.addEventListener('blur', onBlur,true);// model.addEventListener('focusin', onFocus);// model.addEventListener('focusout', onBlur);window.addEventListener('blur', () => {console.log('window blur')console.log(document.activeElement)if (model.contains(document.activeElement)) {onFocus();model.focus()}});</script>
</body>
  • 解决方式二:监听全局focus事件,判断documen.activeElement是否再弹框内,不在的话手动关闭弹框,因为点击iframe后再点击其他元素,焦点就会转移
<body><div class="modal" tabindex="0">model<input type="text" /><input type="file" /><iframe width="300" height="200"></iframe></div><input type="text" /><iframe></iframe><script src="./1.js"></script><script>const model = document.querySelector('.modal')model.setAttribute('tabindex', '-1');function toggle(open) {model.style.display = open ? 'block' : 'none';}let blurTimer = null;function onFocus(event) {console.log('focus')console.log(document.activeElement)//恢复弹框clearTimeout(blurTimer);}function onBlur(event) {console.log('blur')blurTimer=setTimeout(()=>{ toggle(false);})}model.addEventListener('focus', onFocus,true);model.addEventListener('blur', onBlur,true);// model.addEventListener('focusin', onFocus);// model.addEventListener('focusout', onBlur);window.addEventListener('blur', () => {console.log('window blur')console.log(document.activeElement)if (model.contains(document.activeElement)) {onFocus();// model.focus()}});window.addEventListener('focus', (event) => {if (!model.contains(document.activeElement)) {onBlur();}});</script>
</body>

实现点击弹框外关闭弹框功能相关推荐

  1. jq点击按钮打开和关闭弹出层,点击除了当前按钮以外的地方关闭弹出层

    1.html <a id="more" οnclick="moreFun()">更多</a> <ul id="moreL ...

  2. bootstrap中表格、修饰图片、浮动、背景框、提示框及关闭提示框、元素淡入淡出及jQuery中操作类名

    表格: bootstrap中用类定义了几个风格的表格,使用时给table标签加上类名即可,具体如下: 类名 描述 .table 基础表格:标题加粗,只有水平的淡灰色边框线条,没有垂直方向的线条 .ta ...

  3. DialogFragment常见问题(黑色棱角、点击编辑框外关闭软键盘..)

    1.设置对话框外部的背景为完全透明: 在onStart()方法中加入以下代码: @Overridepublic void onStart() {super.onStart();/*** 将对话框外部( ...

  4. 点击弹出窗口外任意地方关闭弹出窗口

    问:如何在点击弹出窗口外其他地方时关闭弹出窗口? 答:使用FlexMouseEvent'sMOUSE_DOWN_OUTSIDE事件.用户点击弹出窗口的外部时,会分发此事件.只需要在popUpWindo ...

  5. 前台alert弹出页面,点击确定,关闭弹出框,整个页面进行刷新数据

    前台alert弹出页面,点击确定,关闭弹出框,整个页面进行刷新数据location.reload(); $("#saveBatch").on("click", ...

  6. element-ui:el-autocomplete实现搜索结果多次点击不关闭弹框

    实现需求 通过搜索,将搜索结果关联到当前页面的对象,原来点击一下就关联,弹框关闭 现在需要支持同一个搜索结果多次点击关联,加快处理速度 实现效果 实现思路 将捕获点击事件,并且阻止传播 @click. ...

  7. js点击取消按钮关闭当前弹框_js关闭当前页面(窗口)的几种方式总结

    1. 不带任何提示关闭窗口的js代码 代码如下: 关闭 2.自定义提示关闭 代码如下: // 这个脚本是 ie6和ie7 通用的脚本 function custom_close(){ if (conf ...

  8. 解决uni-app官方弹框popup关闭不了问题;/pages/extUI/popup/popup;uni-app弹框popup打开调用事件。unin-app弹框封装;

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210716165216781.png?x-oss-process=image/watermark,type_Zm ...

  9. OpenLayers标记地图点及点击地图点显示自定义弹出框

    css代码(设置弹出框样式) /*设置弹出框样式*/.ol-popup {position: absolute;background-color: #eeeeee;-webkit-filter: dr ...

最新文章

  1. asp.net的JSON数据进行序列化和反序列化
  2. 尝鲜Ubuntu Server 12.04 LTS
  3. python对象三个特性_python面向对象的三大特性
  4. CSS3: 移动端开发中 max-device-width 与 max-width 的区别
  5. android退出图标按钮,android-setCloseButtonIcon(位图可绘制)不适用于...
  6. XML:体验学习的乐趣之XML总结
  7. 西南科技大学OJ题 Delete Numbers 0700
  8. 中国移动短信MISC割接
  9. 虚拟机服务器坏处,服务器虚拟化技术的优缺点
  10. STC15单片机定时器0工作模式介绍
  11. MSP430F5529 多路PWM输出控制舵机和电机
  12. myeclipse cracker
  13. (连载)Android 8.0 : 系统启动流程之Linux内核
  14. 有源晶振、石英晶振、陶瓷晶振优缺点分析
  15. 重要的是商业,不是应用(Building a bussiness, not an app)
  16. 推荐一款非常好用的API接口测试工具EoLink
  17. 信息驾驶舱(管理驾驶舱)
  18. 第一次参与国际空间站ISS 的SSTV活动
  19. C语言编写万年历,解决1582年历史问题
  20. 教你如何鉴别原装和组装线孔耳机

热门文章

  1. 【生活】忙忙碌碌的城市中要给予生活以呼吸
  2. 常见威胁建模框架分析与比较
  3. 查看was java版本_查看WAS版本的方式
  4. C++中的Thunk技术 / 非静态类成员函数作为回调函数 的实现方法
  5. 勿忘初心,继续coding
  6. 手写数字识别系统之图像分割
  7. Windows XP 基本操作
  8. 深圳大湾区美丽盛典献礼祖国70华诞——2019世界旅游小姐广东赛区•深港澳赛区联合总决赛华丽收官
  9. 2023南京林业大学计算机考研信息汇总
  10. TOEFL 听力 geology