JavaScript大杂烩11 - 理解事件驱动
- <input type="button" value="Click Me" onclick="demo()"></input>
- <script>
- function demo() {
- alert('click event');
- }
- </script>
当然了也有人直接在onclick属性中写上语句,严重不可取。
- <input id="btnStart" type="button" value="Click Me"></input>
- <script>
- var btnStart = document.getElementById('btnStart');
- btnStart.onclick = function() {
- alert('click event');
- };
- </script>
这里的事件回调函数有个规律,就是回调函数的名字都是事件名前面加了前缀"on"。
- var btnStart = document.getElementById('btnStart');
- var demo = function() {
- alert('click event');
- };
- // 非IE浏览器
- if(btnStart.addEventListener){
- btnStart.addEventListener('click', demo, false);
- }
- // IE浏览器
- if(btnStart.attachEvent){
- btnStart.attachEvent('onclick', demo);
- }
注意上面的代码中attachEvent的第一个参数需要加上个前缀"on",addEventListener第三个参数为false表示事件冒泡时挂接,true表示事件捕获时挂接,attachEvent没有第三个参数,默认就是冒泡时挂接,捕获和冒泡的概念下面会讲到。
- var Events = {
- //添加事件
- add : function(element, type, callback){
- if(element.addEventListener){
- element.addEventListener(type, callback, false);
- }else{
- // 解决document点击的事件处理不一致问题
- element['e'+callback] = function(){
- callback.call(element,window.event);
- };
- element.attachEvent('on' + type, element['e'+callback]);
- }
- },
- //删除事件
- remove : function(element, type, callback){
- if(element.removeEventListener){
- element.removeEventListener(type, callback, false);
- }else if(element.detachEvent){
- element.detachEvent('on' + type, element['e'+callback]);
- }
- },
- //主动的通过程序去触发事件
- dispatch : function(element ,type){
- try{
- if(element.dispatchEvent){
- var evt = document.createEvent('Event');
- evt.initEvent(type,true,true);
- element.dispatchEvent(evt);
- }else if(element.fireEvent){
- element.fireEvent('on'+type);
- }
- }catch(e){};
- }
- };
- var btnStart = document.getElementById('btnStart');
- var demo = function() {
- alert('click event');
- };
- Events.add(btnStart, 'click', demo);
兼容性问题还不止这么多,还有添加多个事件的时候,执行的结果也不大一致,而且对于document的click事件,不同的浏览器反应也是不同,这么的不一致都需要在这个辅助类里面去处理,十分的不方便。所以推荐的方法是使用现有的类库去挂接函数,比如JQuery,这些类库会很好的封装这种差异性,让你完全不去考虑兼容性问题。
- 1. 事件捕获阶段:从可以接收该事件的最外层元素开始询问是否处理该事件,处理完后,该事件则继续一层一层向内层目标元素传递,到达最内层的目标元素时事件捕获阶段结束。
- 2. 目标元素处理阶段: 事件捕获阶段结束后,事件传递到了最内层的目标元素,这个时候目标元素处理该事件,事件处理完后,该事件进入冒泡阶段。
- 3. 事件冒泡阶段:进入冒泡阶段后,事件一层一层向上传递,每一层元素都可以处理该事件,处理完后,事件继续向外层元素传递直到最外层元素,然后结束。
用网上著名的一副图来说明,就是这样:

- <html>
- <head>
- <title></title>
- <style type="text/css">
- #p { width: 300px; height: 300px; padding: 10px; border: 1px solid black; }
- #c { width: 100px; height: 100px; border: 1px solid red; }
- </style>
- </head>
- <body>
- <div id="p">
- parent
- <div id="c">
- child
- </div>
- </div>
- <script type="text/javascript">
- var p = document.getElementById('p'),
- c = document.getElementById('c');
- c.addEventListener('click', function () {
- alert('子节点捕获');
- }, true);
- c.addEventListener('click', function () {
- alert('子节点冒泡');
- }, false);
- p.addEventListener('click', function () {
- alert('父节点捕获');
- }, true);
- p.addEventListener('click', function () {
- alert('父节点冒泡');
- }, false);
- </script>
- </body>
- </html>
鼠标点击一下child元素,观察一下这4个事件的响应顺序来验证一下上面事件传递的3个过程吧。
- p.addEventListener('click', function (evt) {
- var evt = evt || window.event;
- alert(evt);
- alert('父节点捕获');
- }, true);
这个例子的代码改造自上面的例子,下面的所有小例子也是一样。
- c.addEventListener('click', function (evt) {
- var evt = evt || window.event;
- // 方式1: 使用stopPropagation方法
- // evt.stopPropagation();
- // 方式2: 使用cancelBubble属性
- evt.cancelBubble = true;
- }, false);
这样当p元素捕获这个事件并处理后,事件就不再继续传递了。
- var Event = function(event) {
- this.event = event || window.event;
- this.target = (function() {
- var target = this.event.target || this.event.srcElement;
- while (target && target.nodeType == 3) {
- target = target.parentNode;
- }
- return target;
- })();
- this.stop = function(){
- return this.stopPropagation().preventDefault();
- };
- this.stopPropagation = function(){
- if (this.event.stopPropagation) this.event.stopPropagation();
- else this.event.cancelBubble = true;
- return this;
- };
- this.preventDefault = function(){
- if (this.event.preventDefault) this.event.preventDefault();
- else this.event.returnValue = false;
- return this;
- };
- };
这样使用起来就像这样:
- c.addEventListener('click', function (evt) {
- var event = new Event(e);
- event.stopPropagation();
- }, false);
注意这个辅助类Event是工作在回调函数里面,而上面的那个辅助类Events是工作在回调函数外面,帮助挂接回调函数的,不要混淆。
- c.addEventListener('click', function (evt) {
- var evt = evt || window.event;
- // 方式1: 使用preventDefault方法
- // evt.preventDefault();
- // 方式2: 使用returnValue 属性
- evt.returnValue = false;
- }, false);
不用说,又是浏览器的兼容性问题,这个方法我们也加到了上面的那个辅助类中。使用辅助类可以这么写:
- c.addEventListener('click', function (evt) {
- var event = new Event(e);
- event.preventDefault();
- }, false);
不过需要注意,阻止浏览器默认的响应并不会阻止事件继续传递。
- var c = document.getElementById('c');
- c.addEventListener('click', function (evt) {
- var evt = evt || window.event;
- alert(evt.target);
- alert(evt.srcElement);
- alert(this);
- }, false);
4. 获取用户的输入信息
- clientX/clientY:事件发生的时候,鼠标相对于浏览器窗口可视文档区域的左上角的位置;(在DOM标准中,这两个属性值都不考虑文档的滚动情况,也就是说,无论文档滚动到哪里,只要事件发生在窗口左上角,clientX和clientY都是0,所以在IE中,要想得到事件发生的坐标相对于文档开头的位置,要加上 document.body.scrollLeft和 document.body.scrollTop)。
- offsetX,offsetY/layerX,layerY:事件发生的时候,鼠标相对于源元素左上角的位置。
- x,y/pageX,pageY:检索相对于父元素来说,鼠标水平/垂直坐标的值。
- screenX、screenY:鼠标指针相对于显示器左上角的位置。
针对上面的成员,看个简单的例子:
- var c = document.getElementById('c');
- c.addEventListener('click', function (evt) {
- var evt = evt || window.event;
- document.write('button: ' + evt.button + '<br/>');
- document.write('keyCode: ' + evt.keyCode + '<br/>');
- document.write('which: ' + evt.which + '<br/>');
- document.write('altKey: ' + evt.altKey + '<br/>');
- document.write('ctrlKey: ' + evt.ctrlKey + '<br/>');
- document.write('shiftKey: ' + evt.shiftKey + '<br/>');
- document.write('clientX: ' + evt.clientX + '<br/>');
- document.write('clientY: ' + evt.clientY + '<br/>');
- document.write('offsetX: ' + evt.offsetX + '<br/>');
- document.write('offsetY: ' + evt.offsetY + '<br/>');
- document.write('layerX: ' + evt.layerX + '<br/>');
- document.write('layerY: ' + evt.layerY + '<br/>');
- document.write('x: ' + evt.x + '<br/>');
- document.write('y: ' + evt.y + '<br/>');
- document.write('pageX: ' + evt.pageX + '<br/>');
- document.write('pageY: ' + evt.pageY + '<br/>');
- document.write('screenX: ' + evt.screenX + '<br/>');
- document.write('screenY: ' + evt.screenY + '<br/>');
- }, false);
这个例子中的c元素就是上面的嵌套的那个例子中的元素,在不同的浏览器上检查一下输出的数据,体会一下兼容性问题,然后试着把这些属性都加到上面的Event辅助类中去吧。
JavaScript大杂烩11 - 理解事件驱动的更多相关文章
- JavaScript大杂烩12 - 理解Ajax
AJAX缘由 再次谈起这个话题,我深深的记得就在前几年,AJAX被炒的如火如荼,就好像不懂AJAX,就不会Web开发一样.要理解AJAX为什么会出现,就要先了解Web开发面临的问题. 我们先来回忆一下 ...
- JavaScript大杂烩3 - 理解JavaScript对象的封装性
JavaScript是面向对象的 JavaScript是一种基于对象的语言,你遇到的所有东西,包括字符串,数字,数组,函数等等,都是对象. 面向过程还是面向对象? JavaScript同时兼有的面向过 ...
- JavaScript大杂烩1 - 理解JavaScript的类型系统
随着硬件水平的逐渐提高,浏览器的处理能力越来越强大,本人坚信,客户端会越来越瘦,瘦到只用浏览器就够了,服务端会越来越丰满:虽然很多大型的程序,比如3D软件,客户端仍然会存在,但是未来的主流必将是浏览器 ...
- JavaScript大杂烩10 - 理解DOM
操作DOM 终于到了JavaScript最为核心的部分了,通常来说,操作DOM,为页面提供更为友好的行为是JavaScript根本目标. DOM树 - HTML结构的抽象 既然DOM是操纵HTML ...
- JavaScript大杂烩9 - 理解BOM
毫无疑问,我们学习JavaScript是为了完成特定的功能.在最初的JavaScript类型系统中,我们已经分析过JavaScript在页面开发中充当着添加逻辑的角色,而且我们知道JavaScript ...
- JavaScript大杂烩8 - 理解文本解析的"黄金搭档"
文本解析"黄金搭档" - String与RegExp对象 文本解析是任何语言中最常用的功能,JavaScript中也是一样,而正则表达式作为最常用的方式,JavaScript也同样 ...
- JavaScript大杂烩7 - 理解内置集合
JavaScript内置了很多对象,简单的类型如String,Number,Boolean (相应的"值类型"拥有相同的方法),复杂一点的如Function,Object,Arra ...
- JavaScript大杂烩6 - 理解JavaScript中的this
在JavaScript开发中,this是很常用的一个关键字,但同时也是一个很容易引入bug的一个关键字,在这里我们就专门总结一下页面中可能出现的this关键字(包括几种在其他页面文件中出现的this) ...
- JavaScript大杂烩4 - 理解JavaScript对象的继承机制
JavaScript是单根的完全面向对象的语言 JavaScript是单根的面向对象语言,它只有单一的根Object,所有的其他对象都是直接或者间接的从Object对象继承.而在JavaScript的 ...
随机推荐
- 移动端 h5开发相关内容总结(三)
之前写过两篇开发中遇到的问题和解决方案.当时是CSS 和 JavaScript 分开写的.现在写这篇文章的时候感觉很多内容都是有内在联系的,所以不好分开. 给大家分享一下这半年来的感受吧: 知道和理解 ...
- sublime text 前端开发插件安装和配置
前端开发sublimeconfig mac配置 此文件目录中文件主要是关于sublime的插件配置,快捷键配置,主题和字体配置. 插件列表 所有插件都可以使用Package Control安装,具体的 ...
- 【OSX】解决编译AOSP时需要10.5/10.6 SDK下载
有人遇到的是需要10.6的sdk. 公司网快下载了xcode, 把里面的10.5sdk和10.6sdk拿出来, 一共才一百多兆…… 下载链接: http://pan.baidu.com/s/1gdxG ...
- antd tree组件文件名换行 + 点击展开时,自动收起同级其他展开目录
1.在项目中用 antd的tree组件的时候,遇到两个问题 1.文件名太长的话 会超出容器 很难看,解决方法如下 ` 引入css在global下设置 :global { .ant-tree li .a ...
- CSS 基础:文本和字体(4)<思维导图>
这段时间利用一下间隙时间学习了CSS的基础知识,主要目的是加深对CSS的理解,虽然个人主要工作基本都是后台开发,但是个人觉得系统学习一下CSS的基础还是很有必要的.下面我学习CSS时做的思维导图(全屏 ...
- Spring Boot + Spring Cloud 构建微服务系统(六):熔断监控集群(Turbine)
Spring Cloud Turbine 上一章我们集成了Hystrix Dashboard,使用Hystrix Dashboard可以看到单个应用内的服务信息,显然这是不够的,我们还需要一个工具能让 ...
- 扩展方法IEnumerable<T>转换为IList<SelectListItem> ,提供@Html.DropDownList使用
由于在MVC中经常会使用到@Html.DropDownList方法,而该方法接收的是List<SelectListItem> 参数,因此就想着写一个扩展方法,直接把IEnumerable转 ...
- 初识Quartz (一)
首先大概的了解一下Quartz. 一:首先进入官网去看看什么是quartz.http://www.quartz-scheduler.org/ Quartz是一个功能丰富的开源作业调度库,可以集成到几乎 ...
- 深入学习卷积神经网络(CNN)的原理知识
网上关于卷积神经网络的相关知识以及数不胜数,所以本文在学习了前人的博客和知乎,在别人博客的基础上整理的知识点,便于自己理解,以后复习也可以常看看,但是如果侵犯到哪位大神的权利,请联系小编,谢谢.好了下 ...
- Hibernate学习(二)———— 一级缓存和三种状态解析
一.一级缓存和快照 什么是一级缓存呢? 很简单,每次hibernate跟数据库打交道时,都是通过session来对要操作的对象取得关联,然后在进行操作,那么具体的过程是什么样的呢? 1.首先sessi ...