当前位置: 首页 > news >正文

js 事件详解 冒泡

起因:正常情况下我点击s2时是先弹出我是children,再弹出我是father,但是却出现了先弹出我是father,后弹出我是children的情况,这种情况是在和安卓app交互的h5页面中出现的,本地测试没有问题,但是在安卓打包的内嵌h5页面就出现了问题。简单化的代码先展示出来。

html代码如下

<div id="father" class="ss1">s1
    <div id="children" class="ss2">s2
    </div>
</div>

事件绑定如下

复制代码
$('#father').on('click',function (e) {
   alert('我是father')
 })
$('#children').on('click',function (e) {
   alert('我是children')
   e.stopPropagation();
 })
复制代码

 

借此问题,复习了一下js事件,先看一下几个定义

先来看事件注册

复制代码
// IE以外的其他浏览器
// target :文档节点、document、window 或 XMLHttpRequest。 
// type :字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。 
// listener :实现了 EventListener 接口或者是 JavaScript 中的函数。 
// useCapture :是否使用捕捉,一般用 false,事件触发时,会将一个 Event 对象传递给事件处理程序。

target.addEventListener(type,listener,useCapture);//添加
target.removeEventListener(type,listener,useCapture);//删除
复制代码
复制代码
// IE浏览器
// target :文档节点、document、window 或 XMLHttpRequest。 
// type :字符串,事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等。 
// listener :实现了 EventListener 接口或者是 JavaScript 中的函数。

target.attachEvent(type, listener);//添加
target.detachEvent(type, listener);// 移除
复制代码

兼容写法

复制代码
兼容后的方法 
var func = function(){}; 
//例:addEvent(window,"load",func) 
function addEvent(elem, type, fn) { 
    if (elem.attachEvent) { 
        elem.attachEvent('on' + type, fn); 
        return; 
    } 
    if (elem.addEventListener) { 
        elem.addEventListener(type, fn, false); 
    } 
} 

//例:removeEvent(window,"load",func) 
function removeEvent(elem, type, fn) { 
    if (elem.detachEvent) { 
        elem.detachEvent('on' + type, fn); 
        return; 
    } 
    if (elem.removeEventListener) { 
        elem.removeEventListener(type, fn, false); 
    } 
}
复制代码

获取事件对象和事件源(触发事件的元素) 

复制代码
function eventHandler(e){ 
     //获取事件对象 
    e = e || window.event;//IE和Chrome下是window.event FF下是e 
     //获取事件源 
     var target = e.target || e.srcElement;//IE和Chrome下是srcElement FF下是target 
 } 
复制代码

事件委托

复制代码
myTable.onclick = function () { 
    e = e || window.event; 
    var targetNode = e.target || e.srcElement; 
    // 测试如果点击的是TR就触发 
    if (targetNode.nodeName.toLowerCase() === 'tr') { 
        alert('You clicked a table row!'); 
    } 
}
复制代码

事件函数的解除绑定

和事件的绑定其实是相对应的,如果需要接触事件的绑定,运行对应的函数就可以了。如果是原生JS绑定则对应运行removeEventListener()和detachEvent()。

如果是jQuery的bind()和delegate()绑定,也是存在对应的解绑函数用以清除注册事件,比如unbind()和undelegate()。

看一个代码示例:

复制代码
var EventUtil = { 
    //注册 
    addHandler: function(element, type, handler){ 
        if (element.addEventListener){ 
            element.addEventListener(type, handler, false); 
        } else if (element.attachEvent){ 
            element.attachEvent("on" + type, handler); 
        } else { 
            element["on" + type] = handler; 
        } 
    }, 
    //移除注册 
    removeHandler: function(element, type, handler){ 
        if (element.removeEventListener){ 
            element.removeEventListener(type, handler, false); 
        } else if (element.detachEvent){ 
            element.detachEvent("on" + type, handler); 
        } else { 
            element["on" + type] = null; 
        } 
    }              
};
复制代码

再来看看事件流

几个概念

捕获阶段:事件对象通过目标的祖先从传播窗口到目标的父。这个阶段也被称为捕获阶段

目标阶段:本次活动对象到达事件对象的事件的目标。这个阶段也被称为目标阶段。如果事件类型指示事件不起泡,则在完成此阶段后,事件对象将停止。

 

冒泡阶段:事件对象通过目标的祖先中传播以相反的顺序,开始与目标的父和与所述结束窗口。这个阶段也被称为冒泡阶段

默认行为:事件通常由实现作为用户操作的结果分派,以响应任务的完成,或者在异步活动(例如网络请求)期间发信号通知进度。有些事件可以用来控制下一个实现可能采取的行为(或者撤销实现已经采取的行动)。这个类别中的事件被认为是可取消的,他们取消的行为被称为他们的默认行为

取消事件:可取消的事件对象可以与一个或多个“默认动作”相关联。要取消事件,请调用该preventDefault()方法。

一个图片

再上个小demo

复制代码
<ul>
    <li>点我试试</li>
</ul>
<div id="s1" class="ss1">s1
    <div id="s2" class="ss2">s2</div>
</div>
复制代码
复制代码
var ul = document.getElementsByTagName('ul')[0];
var li = document.getElementsByTagName('li')[0];
element.addEventListener(eventfunctionuseCapture) document.addEventListener('click',function(e){console.log('document clicked')},true);//第三个参数为true使用捕获,false为冒泡,false为默认 ul.addEventListener('click',function(e){console.log('ul clicked')},true); li.addEventListener('click',function(e){console.log('li clicked')},true); //IE低版本兼容写法 li.attachEvent('onclick',function(event){ debugger console.log('li clicked'); event.cancelBubble=true; }); s1.addEventListener('click',function () { console.log('s1 捕获方式') },true) s1.addEventListener('click',function () { console.log('s1 冒泡方式') },false) s2.addEventListener('click',function (e) { console.log('s2 捕获方式') // e.stopPropagation(); },true) s2.addEventListener('click',function () { console.log('s2 冒泡方式') },false)
复制代码

点击li时,打印 依次为

1
ul clicked li clicked  

点击s1时,打印依次为

s1 捕获方式    s1 冒泡方式

点击s2时,打印依次为

s1 捕获方式 s2 捕获方式 s2 冒泡方式 s1 冒泡方式

处理事件冒泡和默认事件

1、e.preventDefault()

复制代码
var a = document.getElementById("testA");
a.onclick =function(e){
    if(e.preventDefault){
        e.preventDefault();//
    }else{
        window.event.returnValue = false;//IE
    //注意:这个地方是无法用return false代替的 
    //return false只能取消元素
    }
}
复制代码

2、return false  javascript的return false只会阻止默认行为,而是用jQuery的话则既阻止默认行为又防止对象冒泡。

复制代码
//原生js,只会阻止默认行为,不会停止冒泡
var a = document.getElementById("testA");
a.onclick = function(){
    return false;//当然 也阻止了事件本身
};
//既然return false 和 e.preventDefault()都是一样的效果,那它们有区别吗?当然有。
//仅仅是在HTML事件属性 和 DOM0级事件处理方法中 才能通过返回 return false 的形式组织事件宿主的默认行为。
复制代码
1 //jQuery,既阻止默认行为又停止冒泡
2 $("#testA").on('click',function(){
3     return false;//当然 也阻止了事件本身
4 });

总结使用方法

当需要停止冒泡行为时

复制代码
function stopBubble(e) { 
    //如果提供了事件对象,则这是一个非IE浏览器 
    if ( e && e.stopPropagation ){ 
        e.stopPropagation(); //因此它支持W3C的stopPropagation()方法 
    }else{ 
        window.event.cancelBubble = true; //否则,我们需要使用IE的方式来取消事件冒泡 
    }
}
复制代码

当需要阻止默认事件时

复制代码
function stopDefault( e ) { 
    if ( e && e.preventDefault ){
        e.preventDefault(); //阻止默认浏览器动作(W3C) 
    }else {
        window.event.returnValue = false; //IE中阻止函数器默认动作的方式 
    }
    return false; 
}
复制代码

最后的解决方法:

让我们回顾一下最初的问题,可能部分浏览器把事件的useCapture默认为true,导致点击子元素时父元素的事件先响应了,于是我的办法是在父元素的事件里进行判断

比如容器为#a,动态插入的元素为#b,在#a上监听click事件,判断event.target.id是不是等于b即可,如果.bclass这种,以此类推。

 

我们经常能遇到阻止冒泡,但是阻止捕获一般不会遇到,因为浏览器一般默认就给我们阻止了,只能说什么情况都有啊,万事还是得考虑周全。

相关文章:

  • simple-spring-memcached简介
  • [SPOJ]COT2
  • 设置时间
  • 28次课(使用w查看系统负载、vmstat命令、top命令、sar命令、nload命令)
  • 错误:update 忘了加 where
  • 编程常用动词细微差别
  • lpeg学习笔记- -
  • nslookup工具的使用方法
  • 菜鸟入门【ASP.NET Core】2:部署到IIS
  • 23种简洁好看的扁平化模板
  • TransE论文剩余部分
  • 《Effective C++》 笔记:Tips01-Tips04
  • 实现数据排序的几种方法
  • Laravel整合Bootstrap 4的完整方案
  • Android存储方式之SQLite的使用
  • [case10]使用RSQL实现端到端的动态查询
  • [译] 怎样写一个基础的编译器
  • 【技术性】Search知识
  • 30秒的PHP代码片段(1)数组 - Array
  • Javascript编码规范
  • java小心机(3)| 浅析finalize()
  • JS数组方法汇总
  • MD5加密原理解析及OC版原理实现
  • nginx 负载服务器优化
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • opencv python Meanshift 和 Camshift
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Xmanager 远程桌面 CentOS 7
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 使用API自动生成工具优化前端工作流
  • 使用Swoole加速Laravel(正式环境中)
  • 跳前端坑前,先看看这个!!
  • 一个JAVA程序员成长之路分享
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • # 透过事物看本质的能力怎么培养?
  • $.ajax中的eval及dataType
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (C语言)逆序输出字符串
  • (二)hibernate配置管理
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .gitignore文件_Git:.gitignore
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .Net 中Partitioner static与dynamic的性能对比
  • .net用HTML开发怎么调试,如何使用ASP.NET MVC在调试中查看控制器生成的html?
  • @Autowired和@Resource的区别
  • @Autowired注解的实现原理
  • [AutoSAR系列] 1.3 AutoSar 架构
  • [CISCN2019 华东南赛区]Web4
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷
  • [hdu 2896] 病毒侵袭 [ac自动机][病毒特征码匹配]
  • [hdu4622 Reincarnation]后缀数组