前言

新的一天又开始了,我们对今天对未来抱有很大期待,所以开始我们今天的学习吧,在此之前来点题外话,还是爱好问题。

周三的面试虽然失败,但是也是很有启迪的,比如之前我就从来没有想过爱好问题,我发现我的爱好如下:

① 霹雳布袋戏

完了就米有什么爱好了,其实我完全可以说自己爱好旅游什么的,我也确实爱好旅游,只不过比较穷,走的地方不多罢了,这里又扯出一个话题:

学习工作与生活

我们为了理想需要学习需要工作,工作完了需要生活,现在是前者分量掩盖了后者,所以以后的世界要注意了。

好了进入今天的学习吧,我们今天来看看javascript里面的事件吧。

事件交互

javascript与html之间的交互是通过事件实现的,我们使用侦听器注册事件,
在事件发生时候便执行相关的代码,这就是传说中的观察者模式

事件流

事件捕获与冒泡的出现十分有意思,当浏览器发展到第四代时(IE4),出现了一个小意思的问题:

页面的哪一部分有什么事件?浏览器开发团队也不知道了,想象我们页面的元素:

 <div>
<a>刀狂剑痴<span>叶小钗</span> </a>
</div>

我们点击span时候是不是也点击了a呢?点击a是不是也点击了div呢,依次上升到body与html

事件流描述的是从页面中接收事件的顺序
IE采用事件冒泡流
标准采用事件捕获流
最后addEventLister给出了第三个参数同时支持冒泡与捕获

事件冒泡

由span开始一直传播至document

事件捕获

由document向下传播至span

事件处理程序

事件就是用户或者浏览器自身执行某种动作,比如click、load、mouseover。
而响应某个事件的函数就叫做事件处理程序(事件监听器),事件处理程序的名字以on开头,click=>onclick、load=>onload
 el.addEventListener(eventType, function () {

 }, false);

 该方法应用至dom节点
第一个参数为事件名
第二个为事件处理程序
第三个为布尔值,true便是事件捕获,false为事件冒泡

既然都说到了addEventLister,我们来看一看这段代码吧:

 <html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div>
<a>刀狂剑痴<span id="el">叶小钗</span> </a>
</div>
<script type="text/javascript">
var el = document.getElementById('el');
el.addEventListener('click', function () {
alert('冒泡');
}, false);
el.addEventListener('click', function () {
alert('捕获');
}, true);
</script>
</body>
</html>

我们同时给一个dom绑定了click事件,并且一个是冒泡,一个是捕获,我们的执行顺序是怎么样的呢?

答案一:先冒泡再捕获

但是答案真的是这样的吗?我们将代码改下:

 var el = document.getElementById('el');
el.addEventListener('click', function () {
alert('捕获');
}, true);
el.addEventListener('click', function () {
alert('冒泡');
}, false);

如此一来便是先捕获再冒泡了,我想说这道题真的好耍赖啊。。。。

那么现在我们是离真相近了还是远了呢?其实真相依旧扑所迷离:

 <html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div>
<a id="p">刀狂剑痴<span id="el">叶小钗</span> </a>
</div>
<script type="text/javascript">
var el = document.getElementById('el');
var p = document.getElementById('p');
//el.addEventListener('click', function () {
// alert('捕获');
//}, true);
//el.addEventListener('click', function () {
// alert('冒泡');
//}, false);
p.addEventListener('click', function () {
alert('父级 冒泡');
}, false);
p.addEventListener('click', function () {
alert('父级 捕获');
}, true);
</script>
</body>
</html>

此处会出现两种情况:

① 直接点击a标签,便与之前逻辑一致,哪个定义到前面哪个先执行

② 点击span标签情况会有所不同!!!

我们来理一理:

因为我们span标签没有注册事件,我们点击时候他虽然没有反应,但是他也会向上传递的:

 <html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div>
<a id="p">刀狂剑痴<span id="el" onclick="alert('点击');">叶小钗</span> </a>
</div>
<script type="text/javascript">
var el = document.getElementById('el');
var p = document.getElementById('p'); p.addEventListener('click', function () {
alert('父级 冒泡');
}, false);
p.addEventListener('click', function () {
alert('父级 捕获');
}, true); //el.addEventListener('click', function () {
// alert('捕获');
//}, true);
//el.addEventListener('click', function () {
// alert('冒泡');
//}, false);
</script>
</body>
</html>

我们这个代码执行的顺序是:父级捕获=>点击=>父级冒泡

说明,点击span引发的事件便与事件注册程序的先后无关了,我这里斗胆的猜测一下整个逻辑

标准浏览器默认采用捕获方式,所以:
我们点击span时候,事实上市先点击document然后到span,中间经过了a标签便先执行其逻辑(将事件写到标签中不合适)
 var el = document.getElementById('el');
var p = document.getElementById('p'); p.addEventListener('click', function () {
alert('父级 冒泡');
}, false);
p.addEventListener('click', function () {
alert('父级 捕获');
}, true);
el.addEventListener('click', function () {
alert('冒泡');
}, false);
el.addEventListener('click', function () {
alert('捕获');
}, true);

这段javascript的执行顺序是(点击span):父级捕获=>冒泡=>捕获=>父级冒泡

各位看看这段东西有问题没有,上次要答好寒冬老师这个问题是不可能的。。。

这个问题暂时到这里,若是后面讨论的朋友多我们再研究吧。

【初窥javascript奥秘之事件冒泡】那些年我们一起冒的泡

请使用高版本浏览器

兼容性处理

我们知道这块的兼容性有一点问题,所以我们一般都会封装下的,但是里面的event对象已经target的获取各位就自己想办法处理下吧:

 var EventUtil = {
addHandler: function (el, type, handler) {
if (el.addEventListener) {
el.addEventListener(type, handler, false);
} else {
el.attachEvent('on' + type, handler);
}
},
removeHandler: function (el, type, handler) {
if (el.removeEventListener) {
el.removeEventListerner(type, handler, false);
} else {
el.detachEvent('on' + type, handler);
}
}
};

按道理说,我们本来可以使用call方法将this指向以及event传进去,彻底解决这块兼容问题,但是又涉及到删除时候删不掉而导致性能问题,我们还是老老实实用jquery吧。

真的能写自己框架的话,这块到可以好好研究一番!

事件对象

我们前面经常提到事件对象event,那么他是什么呢?

触发dom上某个事件时,会产生一个事件对象event,这个对象包含着与事件有关的信息
① 导致事件的元素
② 事件类型
③ 特定信息(鼠标信息,键盘信息)
......

这个对象包含太多信息,我这里也记不住,于是挑几个重要的来说吧:

① currentTarget dom 事件处理程序当前正在处理事件的那个元素(始终等于this)

② preventDefault function 取消事件默认行为

③ stopPropagation function 取消事件冒泡

④ target dom 事件的目标(这个与currentTarget不是很好区分哦,currentTarget可能发生变化target却不会)

这里关于currentTarget与target可能有些朋友没有分清楚,我们上个代码吧:

 EventUtil.addHandler(document, 'click', function (e) {
e = window.event || e;
var target = e.target || e.srcElement;
//操作
//此处currentTarget便等于this
//target便是我们点击的元素
alert(e.currentTarget); //HTMLDocument
alert(target); //HTMLSpanElement
});

来吧搞点兼容的代码,反正不吃亏:

 var EventUtil = {
addHandler: function (el, type, handler) {
if (el.addEventListener) {
el.addEventListener(type, handler, false);
} else if (el.attachEvent) {
el.attachEvent('on' + type, handler);
} else {
el['on' + type] = handler;
}
},
removeHandler: function (el, type, handler) {
if (el.removeEventListener) {
el.removeEventListerner(type, handler, false);
} else if (el.detachEvent) {
el.detachEvent('on' + type, handler);
} else {
el['on' + type] = null;
}
},
getEvent: function (e) {
return e ? e : window.event;
},
getTarget: function (e) {
return e.target ? e.target : e.srcElement;
},
preventDefault: function (e) {
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
},
stopPropagation: function (e) {
if (e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
}
};

事件类型

我们一般有这几类事件:

① UI用户界面事件
当用户与页面上元素发生交互时触发 ② 焦点事件
当元素获取/失去焦点时触发 ③ 鼠标事件
鼠标操作引发的事件 ④ 滚轮事件
鼠标滚轮引起的事件,类似的也算 ⑤ 文本事件
text类型的操作 ⑥ 键盘事件
......

我们下面来看看我们的各个事件吧。

UI事件

load事件

当页面全部加载后在window上触发,当所有框架都加载完毕时在框架集上触发,当img加载结束后在img上触发。

javascript中这个家伙非常常见,页面完全加载后(图片、js、css)就会触发,来一个图片加载结束后的提示吧:

 EventUtil.addHandler(img, 'load', function (e) {
e = EventUtil.getEvent(e);
alert(EventUtil.getTarget(e).src + ' 加载结束');
});

PS:此处的加载与我们jquery的document.ready有所不同,一个是图片全部加载结束执行,一个是dom与css形成render树便开始执行,后者是我们希望的。

unload

这个事件与load对应,我们却用到不多,在文档完全卸载后触发(页面切换也会触发)。

这个家伙一般用于释放资源的,我们一般不需要关注。

resize

这个事件我们也用得很多,当浏览器高度或者宽度变化时就会触发(这个时候也会引起页面回流reflow哦),事件在window上触发。

这个事件中的代码可能会被频繁执行,所以定义时候一定要注意。

scroll

滚动条滚动时候触发

焦点事件

① blur: 事情焦点时候触发,不会冒泡

② focus: 获取焦点时候触发,不会冒泡

其它的略去

鼠标事件

事件很多......

后面还有很多新增的事件,我这里先不管啦。。。等接触到移动开发再研究吧,今天暂时这样。

结语

今天我们一起学习了javascript的事件流,各位有所得么?

【追寻javascript高手之路05】理解事件流的更多相关文章

  1. 【追寻javascript高手之路04】理解prototype

    前言 中午时候我去药店称了下体重,好家伙!我减肥成功了,足足比上个月瘦了10斤!于是想减肥就去郑州吧... 然后回来迷迷糊糊睡了一会,居然想起了周三的面试,有点小遗憾有点小触动. 这次回成都后,还没有 ...

  2. 【追寻javascript高手之路03】javascript对象大乱斗

    前言 昨天我们学习了下javascript中函数的参数与作用域的东东,现在回过头来看,其实都是与函数相关的,所以javascript中的函数仍然是王道,我们今天大概会发二篇或者三篇博客一起来巩固我们的 ...

  3. 【追寻javascript高手之路01】javascript参数知多少?

    前言 我最近在思考一个问题,我本身平时还是积累了不少东西,面试时候问的东西基本逃不出写的博客(当然,高级阶段的就不行了),但是真的被问到时我却不一定答得上来. 知道且能回答,回答的效果都不是很好... ...

  4. 【追寻javascript高手之路02】变量、作用域知多少?

    前言 本来想把这个与上篇博客写到一起的,但是考虑到是两个知识点还是分开算了,于是我们继续今天的学习吧. 基本类型与引用类型 ECMAScript的的变量有两种类型: 基本类型(值类型):简单数据段 引 ...

  5. [转]as3事件流机制彻底理解

    题记: 看过网上一些as3事件流的教程,觉得大多都讲得不甚清楚,让人不能直观的理解事件流.而这篇教程以将事件流过程比喻成捕鱼过程,形象简单. 在此基础上对于as3事件流总算有了全面的理解.事件流机制说 ...

  6. DOM事件揭秘-事件流

    事件:文档/窗口中发生的特定的交互瞬间 瀑布流,图片轮播 动作都是通过事件触发的 课程内容: 1,理解事件流 2,使用时间处理程序 3,不同的事件类型 ie4.0以后, 事件流:描述的是从页面中接收事 ...

  7. 深入理解javascript事件流

    摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原 ...

  8. [js高手之路] javascript面向对象写法与应用

    一.什么是对象? 对象是n个属性和方法组成的集合,如js内置的document, Date, Regexp, Math等等 document就是有很多的属性和方法, 如:getElementById, ...

  9. [js高手之路] vue系列教程 - 事件专题(4)

    本文主要讲解事件冒泡,事件绑定的简写,事件默认行为,按键码等一系列与事件相关的知识. 一.事件绑定的简写,@事件类型.  之前我的[js高手之路] vue系列教程 - vue的事件绑定与方法(2) 用 ...

随机推荐

  1. QQ5.0左侧滑动显示效果

    前三篇为大家介绍了如何实现简单的类QQ5.0左侧的侧滑效果,本篇我将带领大家一起探讨一下如何真正实现QQ5.0左侧的侧滑效果,对于本篇的内容与之前的三篇关联性很强,如果前三篇你已经完全掌握,对于这一篇 ...

  2. JavaScript闭包(二)——作用

    一.延迟调用 当在一段代码中使用 setTimeout 时,要将一个函数的引用作为它的第一个参数,而将以毫秒表示的时间值作为第二个参数. 但是,传递函数引用的同时无法为计划执行的函数提供参数.可以在代 ...

  3. 从css3书写顺序引出来的border-radius参数

    本鱼表示偶已经不会取标题了... 当时写这篇文章主要是想探讨一下优雅降级和渐进增强的区别,按照正常的逻辑思维,不管是降级还是增强,应该对于效果是没什么区别的,因为后者会覆盖前者,但今天无意看到张鑫旭的 ...

  4. Android基于mAppWidget实现手绘地图(十四)–在一个应用中使用多个地图

    使用切图工具创建不同名称的地图资源.然后将这些资源放置到assert文件夹内. 像下面这样: 在代码中,根据不同的地图名称,查找地图. map = new MapWidget(this, " ...

  5. C#语法糖(Csharp Syntactic sugar)大汇总

    首先需要声明的是"语法糖"这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换:而且可以提高开发编码的效率,在性能上也不会带来损失.这让java开发人员羡慕 ...

  6. SpringIOC使用扩展

    在上篇博客中,我们使用Spring通过setter访问器实现了对属性的赋值,这种做法被称为设值注入.除此之外Spring还提供了通过构造方法赋值的能力,成为构造注入.下面我们通过一个小demo来了解如 ...

  7. Node.js系列基础学习----安装,实现Hello World, REPL

    Node.js基础学习 简介 简单的说 Node.js 就是运行在服务端的 JavaScript.Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台.Node.js是一 ...

  8. DirectX.Capture Namespace

    DirectX.Capture Class Library   DirectX.Capture Namespace The DirectX.Capture namespace contains cla ...

  9. 代码实现SQL Server动态行转列,不用存储过程

    分两步查询,第一步查询出动态列,第二步使用PIVOT函数. 代码: List<DataTable> dataTableList = new List<DataTable>(); ...

  10. 在使用vs2012中的MVC4出的错误!类型“System.Data.Entity.DbContext”在未被引用的程序集中定义

    理解决方案的nuget程序包2.选中已安装的包,找到EntityFramework,点击管理 由于我是在Taobao_DAL中用到了EF的“数据上下文对象”,他报错了.所以我们要选中它,将EF5.0安 ...