起因:正常情况下我点击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时,打印 依次为

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这种,以此类推。

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

一次关于js事件出发机制反常的解决记录的更多相关文章

  1. js事件循环机制辨析

     对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的 ...

  2. 这可能是最简明扼要的 js事件冒泡机制+阻止默认事件 讲解了

    哎 js事件冒泡机制和阻止冒泡 阻止默认行为好像永远也整不清楚,记了忘 忘了记...醉了 这篇文章写完以后下次再忘记 就呼自己一巴掌,忘一次一巴掌 首先要明白两个概念——事件和事件流 事件指的是用户或 ...

  3. JS 事件循环机制 - 任务队列、web API、JS主线程的相互协同

    一.JS单线程.异步.同步概念 从上一篇说明vue nextTick的文章中,多次出现“事件循环”这个名词,简单说明了事件循环的步骤,以便理解nextTick的运行时机,这篇文章将更为详细的分析下事件 ...

  4. JS事件冒泡机制和兼容性添加事件

    本篇文章主要来讲讲 事件的冒泡机制 和 添加事件的几种方法. 一. JS的时间传递顺序: 捕获阶段 -> 目标阶段 -> 冒泡阶段 捕获阶段是指从父层往子层找.比如 <body> ...

  5. js 事件循环机制 EventLoop

    js 的非阻塞I/O  就是由事件循环机制实现的 众所周知  js是单线程的 也就是上一个任务完成后才能开始新的任务 那js碰到ajxa和定时器.promise这些异步任务怎么办那?这时候就出现了事件 ...

  6. js事件流机制冒泡和捕获

    JavaScript与HTML之间的交互是通过事件实现的.事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间. 事件流 从页面中接收事件的顺序称为事件流. IE --> 事件冒泡流 Netsc ...

  7. js事件循环机制(Event Loop)

    javascript从诞生之日起就是一门  单线程的  非阻塞的  脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event lo ...

  8. Node.js 事件循环机制

    Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...

  9. js事件循环机制 (Event Loop)

    一.JavaScript是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决 ...

随机推荐

  1. CoreJava逻辑思维-顺时针打印自定义矩阵

    CoreJava逻辑思维-顺时针打印自定义矩阵 这两天回顾了一下刚入Java时的一些比较有意思的逻辑题,曾经也费劲脑汁的思考过的一些问题,比如百钱百鸡最简单的算法啦之类的,而今天博主想说的是一个循环打 ...

  2. log4go的输出优化

    又看了一些golang的日志包和相关的文章,仔细阅读了go 1.9.2系统提供的log和go-log,产生了对log4go的日志输出进行优化的想法. 结构化与multiwriter log使用mult ...

  3. 关于在Windows下Composer下载安装Yii2.0

    先是composer的安装,主要有两个方式,一个直接下载安装包安装,Composer-steup.exe文件,第二种直接下载composer.phar文件,用php去运行这个文件可以一样起到作用,之后 ...

  4. JavaScript操作符(布尔操作符、乘性操作符和加性操作符)

    布尔操作符 布尔操作符用来测试两个值的关系,布尔操作符有三个,逻辑非(!).逻辑与(&&),逻辑或(||). 逻辑非由一个叹号(!)组成,可以应用于JavaScript任何值.逻辑非首 ...

  5. 手把手教你用vue-cli搭建vue项目

    手把手教你用vue-cli搭建vue项目 本篇主要是利用vue-cli来搭建vue项目,其中前提是node和npm已经安装好,文章结尾将会简单提到一个简单的例子.使用vue-cli搭建项目最开始我也是 ...

  6. Asp.Net Web API(二)

    创建一个Web API项目 第一步,创建以下项目 当然,你也可以创建一个Web API项目,利用 Web API模板,Web API模板使用 ASP.Net MVC提供API的帮助页. 添加Model ...

  7. PDO绑定含IN的SQL语句的参数注意事项

    PDOStatement::bindParam(),表示将PDO::prepare()语句中的占位符用参数替换掉.注意,在绑定含有IN的SQL多参数语句时要额外小心,比如$stmt = $db-> ...

  8. mysql 先分组在排序

    mysql语句的语法模板: select distinct <select_list> from <left_table><join_type> join < ...

  9. 后缀数组之hihocoder 重复旋律1-4

    蒟蒻知道今天才会打后缀数组,而且还是nlogn^2的...但基本上还是跑得过的: 重复旋律1: 二分答案,把height划分集合,height<mid就重新划分,这样保证了每个集合中的LCP&g ...

  10. Android破解学习之路(六)——Android游戏 方块冒险 破解

    前言: 可能大家看到标题会有些懵逼,以为我发错了,这应该是五才对吧,其实,五我已经发了,不过被管理大大移出首页了,不知道这一篇是不是也会是同样的命运.. 今天所写的是关于支付宝内购的破解 原版 链接: ...