Js中的DOM事件

来源:互联网 发布:图书管理系统java思路 编辑:程序博客网 时间:2024/06/06 15:05

我们大家都知道,人与人之间的交流可以通过语言,文字,肢体动作,面部微表情等,但是你知道Javascript和HTML之间是通过什么进行交互的么?你又知道Javascript和HTML之间是如何进行交互的么?如果你不是那么清楚,可以看这篇文章。。。

前面的话:

这篇博文由浅入深,先介绍两种事件流,然后介绍常见的事件处理程序以及它们之间的差异(涉及到一个简单的兼容的处理函数),后面还会针对事件对象进行深入学习,最后则是通过DOM事件实现的小案例。

事件流:

关于事件流,简单的来说就是:事件流描述的是从页面中接受事件的顺序。下面是《Javascript高级程序设计》里面的说法:

当浏览器发展到第四代时(IE4和Netscape Communicator 4),浏览器团队遇到一个很有意思的问题:页面的哪一部分会拥有特定的事件?想象下在一张纸上有一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是一组圆。两家公司的开发团队在看待浏览器事件方面还是一致的。如果你单击了某个按钮,那么同时你也单击了按钮的容器元素,甚至整个页面。

事件流描述的是从页面中接受事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了两个截然相反的事件流概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。

事件冒泡

IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点。看下面的例子

复制代码
<!DOCTYPE html><html>    <head>        <meta charset="UTF-8">        <title>事件流</title>    </head>    <body>        <input type="button" id="btn" value="" />    </body></html>
复制代码

点击按钮,那么这个click事件会按照这样传播:
<input-->body-->html-->document

事件捕获

Netscape团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件。

针对上面同样的例子,点击按钮,那么此时click事件会按照这样传播:
document-->html-->body-->input

虽然事件捕获是Netscape唯一支持的事件流模型,但IE9、Safari、Chrome、Opera和Firefox目前也都支持这种事件流模型。但由于老版本的浏览器不支持,因此很少有人使用事件捕获。

事件处理程序

HTML事件处理程序

HTML事件处理程序(现在不建议使用了):事件直接加在HTML代码中,例如:

<input type="button" id="btn" value="HTML事件处理程序" onclick="alert('HTML事件处理程序')" />

或者:

<input type="button" id="btn1" value="HTML事件处理程序" onclick="demo1()" />function demo1(){    alert("HTML事件处理程序");}

缺点:HTML和js代码高耦合,如果修改,就要修改两个地方--HTML元素内和script函数。

DOM0级事件处理程序

DOM0级事件处理程序,是一种较传统的方式:把一个函数赋值给一个事件的处理程序属性。例如:

var btn2 = document.getElementById("btn2");btn2.onclick = function(){    alert("DOM0级事件处理程序");}

现在用得比较多,优势是简单(只需要考虑JS不考虑HTML)且跨浏览器。如果想取消点击事件,将其设为null即可。

var btn2.onclick = null;//这时点击不会弹出窗口,表示已经取消了该点击事件

下面是我学习时候的一些思考:

思考一:DOM0级事件是否可以同时添加多个不同事件处理方法

复制代码
var btn2 = document.getElementById("btn2");btn2.onclick = function(){    console.log("DOM0级事件处理程序-click-01");}btn2.onmouseover = function(){    console.log("DOM0级事件处理程序-mouseover");}
复制代码

结果:鼠标经过,控制台出现:DOM0级事件处理程序-mouseover;鼠标点击,控制台出现:DOM0级事件处理程序-click-01。
说明:DOM0级事件可以同时添加多个不同事件处理方法

思考二:DOM0级事件是否可以同时添加多个相同事件处理方法

我们在前面的基础上添加如下代码:

btn2.onclick = function(){    console.log("DOM0级事件处理程序-click-02");}

结果:鼠标经过,控制台出现:DOM0级事件处理程序-mouseover;鼠标点击,控制台出现:DOM0级事件处理程序-click-02。(在这里你可能以为会出现DOM0级事件处理程序-click-01和DOM0级事件处理程序-click-02)
说明:DOM0级事件不可以同时添加多个相同事件处理方法(这样说好像不太严格,大概意思就是直接忽略了前面的onclick,只显示最后的onclick的结果。
那么这里就设下一个疑问:我们应该如何让DOM同时绑定多个相同事件呢???我们接着往下看

DOM2级事件处理程序

DOM2级事件处理程序定义了两个方法--用于处理指定和删除事件处理程序的操作:

  addEventListener()添加事件监听程序

  removeEventListener()移除事件监听程序

  三个参数设置;事件名称,处理方法(函数),布尔值--false:表示在冒泡阶段调用事件处理程序(可以最大限度的兼容浏览器),一般设为false;true表示在捕获阶段调用事件处理程序

来看下面的例子:

var btn3 = document.getElementById("btn3");btn3.addEventListener('click',function(){    console.log("DOM2级事件处理程序-click-01");},false)//DOM2级事件处理程序-click-01

与DOM0级不同的是,我们移除需要使用removeEventListener(),代码如下:

复制代码
var btn3 = document.getElementById("btn3");btn3.addEventListener('click',function(){    console.log("DOM2级事件处理程序-click-01");},false)btn3.removeEventListener('click',function(){    console.log("DOM2级事件处理程序-click-移除成功");},false)//DOM2级事件处理程序-click-01
复制代码

在这里,你很有可能和我想的一样,DOM2级事件处理程序已经移除完了。但是结果是DOM2级事件处理程序-click-01依然还在,按照理论上来说不应该是被移除了么?但是为什么还在?

原因:通过addEventListener方法添加的事件处理程序只能用removeEventListener来移除,移除时传入的参数与添加处理程序时使用的参数相同。这就意味着通过addEventListener添加的匿名函数将无法移除。真的是这样么?我们继续来看例子:

复制代码
var btn3 = document.getElementById("btn3");var handler = function demo3(){    alert("DOM2级事件处理程序");}btn3.addEventListener('click',handler,false)//在不加后面的代码的时候弹框显示DOM2级事件处理程序btn3.removeEventListener('click',handler,false)//加了removeEventListener后点击,没有弹框,说明该点击事件已经被移除了
复制代码

接下来我们同样思考几个问题:
思考一:DOM2级事件是否可以同时添加多个不同事件处理方法

复制代码
var btn3 = document.getElementById("btn3");btn3.addEventListener('click',function(){    console.log("DOM2级事件处理程序-click-01");},false)btn3.addEventListener('mouseover',function(){    console.log("DOM2级事件处理程序-mouseover");},false)
复制代码

结果:鼠标经过,控制台出现:DOM2级事件处理程序-mouseover;鼠标点击,控制台出现:DOM2级事件处理程序-click-01。
说明:DOM2级事件可以同时添加多个不同事件处理方法

思考二:DOM2级事件是否可以同时添加多个相同事件处理方法

复制代码
var btn3 = document.getElementById("btn3");btn3.addEventListener('click',function(){    console.log("DOM2级事件处理程序-click-01");},false)btn3.addEventListener('mouseover',function(){    console.log("DOM2级事件处理程序-mouseover");},false)btn3.addEventListener('click',function(){    console.log(this.id);},false)
复制代码

结果:鼠标经过,控制台出现:DOM2级事件处理程序-mouseover;鼠标点击,控制台出现:DOM2级事件处理程序-click-01和btn3。
说明:DOM2级事件可以同时添加多个不同事件处理方法,同时在这里可以用this引用被触发的元素。

注意:
  1、要处理的事件名(事件名去掉“on”,如点击事件需写成click);
  2、作为事件处理程序的函数名(函数不需要带括号,只要函数名或者直接写函数体也可以);
  3、布尔值(true表示使用事件捕获流,false表示使用事件冒泡流,一般使用false兼容各种浏览器)。
  4、通过addEventListener添加的事件只能通过removeEventListener来移除;DOM 0级和DOM 2级事件可以在一个DOM上添加多个事件,按顺序执行
  5、DOM2级事件处理程序适用于:IE9、Firefox、Safari、Chrome和Opera


思考:那么如果我们想要兼容低版本的IE该怎么办呢?请继续往下阅读

IE事件处理程序

(声明:IE事件处理程序的代码没有进行测试)

IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent(),这两个方法接受相同的两个参数:事件处理程序名称与事件处理函数程序函数(因为IE8及更早版本只支持时间冒泡,所以没有第三个参数)。下面我们来看代码:

var btn4 = document.getElementById("btn4");btn4.attachEvent('onclick',function(){    console.log("IE事件处理程序-onclick-01");});

这里需要注意的是,第一个参数是onclick,而不是click。另外与DOM0级中的不同在于作用域,看下面的代码:

var btn4 = document.getElementById("btn4");btn4.attachEvent('onclick',function(){    console.log(this === window);  //true});

其他需要注意的有:
1、attachEvent方法也可以为一个元素添加一个或多个事件处理程序,当添加多个相同的事件处理程序的时候,这些事件处理程序不是以添加它们的顺序而执行,而是以相反的顺序被触发。比如下面这个例子:

复制代码
var btn4 = document.getElementById("btn4");btn4.attachEvent('onclick',function(){    console.log("hello");});btn4.attachEvent('onclick',function(){    console.log("World");});
复制代码

这段代码用DOM0级的方法得到的结果为World,用DOM2级的方法得到结果为先是hello然后是World,而在这里的结果为先是World然后是hello。

2、detachEvent()方法和DOM2级中移除的方法一样,同样也需要注意中间添加的匿名函数不能被移除,这里就不再举例说明了。

从HTML事件处理程序到DOM0级事件处理程序,再到DOM2级处理程序,还有IE事件处理程序,我们在使用的时候为了让用户体验最佳,我们该让我们的代码兼容各种浏览器,那么我们有什么好的方法让我们的代码兼容到各种浏览器中呢?

跨浏览器的事件处理程序

为了解决跨浏览器的问题,我们不得不集中的把前面几种方法柔和在一起,然后就出现了跨浏览器的事件处理程序。(我的思路如下)

    1、先考虑添加事件的情况,在添加的时候,我们要知道给谁添加?添加的是什么事件?然后事件触发的是什么函数?
        给谁添加?当然是我们的元素了,因为还不知道,这里我们先设置为element
        添加的是什么事件?click?onclick?onmouseover?我们也还是不知道,先设置为type吧
        添加什么函数?前面的例子中有用到过handler这里也先就用这个

function(element, type, handler) {……}

    2、我们要判断我们的浏览器是否支持相关的操作,这里我们可以用if-else来判断

复制代码
if(element.addEventListener) {    //DOM2级事件处理程序} else if(element.attachEvent) {    //IE事件处理程序} else {    //DOM0级事件处理程序}
复制代码

    3、针对DOM2级事件处理程序、IE事件处理程序和DOM0级事件处理程序它们有相似的地方,然后我们通过前面可以知道,它们对于事件的处理有点不和谐,我们该怎样呢?很明显,添加"on"比去掉"on"更简单,所以比如点击事件的type应该为click,然后在需要用onclick的地方,使用'on'+type,然后各自保留各自的特点    
DOM2级事件处理程序:element.addEventListener(type, handler, false);
IE事件处理程序:element.attachEvent('on' + type, handler);
DOM0级事件处理程序:element['on' + type] = handler;

    4、到这里基本的添加情况以完成,接下来用同样的方法来考虑移除事件的情况(步骤省略)

    5、然后分别给添加事件函数和移除事件函数进行命名addHandler和deleteHandler,并封装起来,得到我们的最终效果:

复制代码
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;        }    },    deleteHandler: 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;        }    }}
复制代码

事件对象

DOM中的事件对象

  前面我们已经封装好一个函数了,我们后面的操作直接引用前面的那个函数。说到事件对象,我们先不得不说事件对象的两个重要属性type和target
  其中type是获取事件的类型,而target是获取事件的目标。直接看代码:

var btn = document.getElementById("btn");eventUtil.addHandler(btn,'click',showMes);function showMes(event){    alert(event.type);//结果是弹出click}

结果是弹出click,说明了该事件的类型是点击事件

var btn = document.getElementById("btn");eventUtil.addHandler(btn,'click',showMes);function showMes(event){    alert(event.target);//结果是弹出[object HTMLInputElement]}

结果是弹出[object HTMLInputElement],说明该事件的目标是input元素

下面我们运行一段代码,来看一个有趣的现象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="box">
    <input type="button" id="btn" value="DOM中的事件对象" />
    <a href="www://baidu.com" id="go">百度</a>
</div>
 
function showMes(event){
    alert(event.target.nodeName);
}
 
function showBox(){
    alert("这是放盒子的box")
}
 
var btn = document.getElementById("btn");
var box = document.getElementById("box");
 
eventUtil.addHandler(btn,'click',showMes);
eventUtil.addHandler(box,'click',showBox);

 运行结果:鼠标点击按钮,分别弹出了“INPUT”和“这是放盒子的box”,我们只点击了按钮,为什么按钮所在的div也会显示呢?查资料知道这是因为事件冒泡机制导致的。

那么我们该如何阻止事件冒泡呢?

我们的事件对象给我们提供了一种方法:通过stopPropagation()方法来实现,代码示例如下:

复制代码
function showMes(event){    alert(event.target.nodeName);    event.stopPropagation();//阻止事件冒泡}function showBox(){    alert("这是放盒子的box")}var btn = document.getElementById("btn");var box = document.getElementById("box");eventUtil.addHandler(btn,'click',showMes);eventUtil.addHandler(box,'click',showBox);
复制代码

光知道阻止事件冒泡行为还不行我们还需要知道的一种方法:阻止事件默认行为-->preventDefault()方法

比如我们知道的a标签有一个默认行为就是跳转到目标地址,如果我们想要阻止这种行为我们就可以使用该方法,代码如下:

复制代码
<a href="www://baidu.com" id="go">百度</a>var go = document.getElementById("go");function showGo(event){    event.stopPropagation();//阻止事件冒泡    event.preventDefault();//阻止事件的默认行为}eventUtil.addHandler(go,'click',showGo);
复制代码

运行结果是,无论怎样点击,都跳转不到百度页面。
在a标签里面阻止事件默认行为还有一种方法就是直接在a标签里面添加,比如<a href="javascript:;">aa</a>

补充:在需要通过一个函数处理多个事件时,可以使用type属性,比如:

复制代码
var btn = document.getElementById("btn");var handler = function(event){    switch(event.type){        case"click":            alert("Clicked");            break;                case"mouseover":            event.target.style.backgroundColor = "red";            break;                case"mouseout":            event.target.style.backgroundColor = "";            break;    }};btn.onclick = handler;btn.onmouseover = handler;btn.onmouseout = handler;
复制代码

IE中的事件对象

  【type属性】用于获取事件类型
  【srcElement属性】用于获取事件的目标(DOM中用的是target),所以,可以使用

event=event || window.event;(考虑IE8以前的版本)var ele=event.target  event.srcElement;(考虑非IE与IE)

  【cancelBubble属性】用于阻止事件冒泡,设为true表示阻止冒泡,false表示不阻止冒泡。
  【returnValue属性】用于阻止事件的默认行为,设为false表示阻止。
  这里只介绍IE事件对象的常用属性,关于它的具体例子就不做介绍了

跨浏览器的事件对象:

搞清楚前面的问题,下面的一段代码看起来就简单了。它直接将前面提到的所有方法和属性一起封装起来,并保存到一个单独的js文件中,命名为event.js,其代码如下:

复制代码
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;        }    },    //获取事件    getEvent: function(event) {        return event ? event : window.event;    },    //获取事件类型    getType: function(event) {        return event.type;    },    //获取元素    getElement: function(event) {        return event.target || event.srcElement;    },    //阻止事件默认行为    preventDefault: function(event) {        if(event.preventDefault) {            event.preventDefault();        } else {            event.returnValue = false;        }    },    //阻止事件冒泡    stopPropagation: function(event) {        if(event.stopPropagation) {            event.stopPropagation();        } else {            event.cancelBubble = true;        }    }}
复制代码

 我们可以用如下代码来测试点击a事件

复制代码
window.onload=function(){  var go=document.getElementById('go'),      box=document.getElementById('box');  eventUtil.addHandler(box,'click',function(){      alert('我是整个父盒子');  });  eventUtil.addHandler(go,'click',function(e){      //e=eventUtil.getEvent(e);      e=e || window.event;      alert(eventUtil.getElement(e).nodeName);      eventUtil.preventDefault(e);      eventUtil.stopPropagation(e);  });}
复制代码

 

事件类型

因为事件类型比较多,这里主要通过两个具体的案例来展示,不做详细介绍

案例一:用鼠标或键盘事件来控制抽奖。

效果截图:

查看地址:http://sandbox.runjs.cn/show/0pibyaes

案例二:模仿扣扣登陆时的拖曳效果(主要学习其中如何实现拖曳效果的思维)

效果截图:

查看地址:http://sandbox.runjs.cn/show/7mhb7pfw

更多参考资料:

  参考书籍:《Javascript高级程序设计(第三版)》

  参考博客: https://my.oschina.net/u/1403185/blog/184960

    http://www.cnblogs.com/luozhihao/p/5934935.html

    http://www.cnblogs.com/xiaohuochai/p/5867195.html

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 工作调动没啥消息怎么办 裁剪刀老是推歪怎么办 衣服上粘胶水了怎么办 衣服钻掉了有胶怎么办 衣服上贴纸掉了怎么办 裤子沾上502胶水怎么办 衣服上沾泡沫胶怎么办 衣服上面滴上502怎么办 502胶水弄衣服上怎么办 裤子上粘了胶怎么办 胶水滴在衣服上怎么办 衣服上有502胶水怎么办 衣服上面粘了胶怎么办 衣服上的胶干了怎么办 凌晨4点到火车站怎么办 运管罚款没钱交怎么办 郑州地铁票没买怎么办 遇到吸毒者拦路威胁要钱怎么办 开车遇见拦路要钱的怎么办 高速上有人拦车怎么办 马路上有人拦车怎么办 苹果手机下截软件要钱怎么办 孩子在学校问同学要钱怎么办 在学校被同学要钱怎么办 把人家店砸了要怎么办 外汇出金不到账怎么办 把罚款单弄丢了怎么办 在12306买不到下铺怎么办有 地铁票买反了怎么办 香港买错特惠票怎么办 到达迪拜t3 后怎么办 海藻面膜调多了怎么办 被鸡爪子抓伤了怎么办 被鸡抓伤肿了怎么办 护士电子化没有激活码怎么办 窗帘盒螺丝掉了怎么办 窗帘的环扣掉了怎么办 门式起重吊装行车脱轨怎么办 在日本丢了东西怎么办 在日本钱包丢了怎么办 被起诉后没钱还怎么办