JavaScript事件

事件流

事件流描述的是从页面中接受事件的顺序。但是IE和Netscape开发团队提出了差不多相反的事件流的概念。IE的事件流是事件冒泡,而Netescape Communicator的事件流是事件捕获。

  • 事件冒泡

IE的事件流叫做事件冒泡,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点。

  • 事件捕获

事件捕获的思想是不太具体的节点应该更早接受到事件,而最具体的节点应该最后接收到事件。事件捕获的泳衣在于事件到达预定目标前捕获它。
但是由于老版本不支持,因此很少有人用事件捕获,在特殊时候再利用事件捕获,一般都可以利用事件冒泡。

  • DOM事件流

"DOM2级事件"规定的事件流包括了三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是世纪的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
在DOM的事件流中,实际的目标在捕获阶段不会接收到事件。下一个阶段是处于目标阶段,于是事件在目标上发生,并且在事件处理中看作是冒泡阶段的一部分。然后冒泡阶段发生,事件又传播回文档。

事件处理程序

事件处理就是用户或者浏览器自身执行的某种动作。例如click、load和mouseover等,都是事件的名字。而响应某个事件的函数就叫做事件处理程序,也叫做事件侦听器。

为事件指定的处理程序有如下几种:

  • HTML事件处理程序

某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定的。这个特性的值应该是能够执行JavaScript代码。

  • DOM0级事件处理程序

通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,而且至今仍然为所有现代浏览器所支持。要使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。

  • DOM2级事件处理程序

"DOM2"事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addeventListener()和removeEventListener()。所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。如果最后这个布尔值参数是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
比如要在按钮上为click事件添加事件处理程序,可以使用以下代码:

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
    console.log(this.id);
}, false);

此事件会在冒泡阶段被触发,此时和DOM0级方法一样,这里添加的事件处理程序也是其依附的元素的作用域中运行。使用DOM2级添加方法的主要好处是能够添加多个事件处理程序。

  • IE事件处理程序

IE实现了和DOM类似的两个方法,attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。由于IE8以及更早的版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。在使用DOM0级方法的时候,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法时候,作用域为全局作用域,因此this等于window,例如:

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

同时与addEventListener()类似,也可以为一个元素添加多个事件处理程序。

  • 跨浏览器的事件处理程序

为了跨浏览器的方式处理事件,不少开发人员会使用能够隔离浏览器差异的JavaScript库。要保证处理事件的代码能在大多数浏览器下一致运行,只需要关注冒泡阶段。
第一个要创建的方法是addHandler(),作用是根据情况分别使用DOM0级方法、DOM2级方法或者IE方法来添加事件。这个函数接受3个参数:要操作的元素、事件名称和事件处理程序函数。与之相对应的是removeHandler(),也接受相同的参数。

事件对象

在触发DOM上的某个事件时候,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。所有浏览器都支持event对象,但是支持方式不同。

  • DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。
例如

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    console.log(event.type); // "click"
};
btn.addEventListener("click", function(){
    console.log(event.type); // "click"
}, false);

这两个例子都会显示事件的类型,这个属性始终都会包含被触发的事件类型。
在通过html特性指定事件处理程序时,变量event中保存着event对象,比如

<input type = "button" value = "Click me" onclick = "alert(event.type)" />

这种方式提供event对象,可以让html特性事件处理程序与javascript函数执行相同的操作
在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值。比如

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    console.log(event.currentTarget === this); //true
    console.log(event.target === this);  //true
};

这个例子检验了currentTarget和target与this的值,由于click事件的目标是按钮,因此这三个值是相等。如果事件处理程序存在与按钮的父节点中(例如document,body中)那么这些值是不相等的。如下例子

document.body.onclick = function(event){
    console.log(event.currentTarget === document.body); //true
    console.log(this === document.body); //true
    console.log(event.target === document.getElementById("myBtn")); //true
}

当单击这个例子中的按钮时候,this和currentTarget都等于document.body,因为事件处理的程序是注册到这个元素上的,但是target的元素却等于按钮元素,是因为它是click的真正目标。
在需要通过一个函数处理多个事件的时候,可以使用type属性。例如

var btn = document.getElementById("myBtn");
var handler = function(event){
  switch(event.type){
    case "click":
      console.log("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;

利用handler函数来处理3中不同的事件。这里可以通过检测event.type属性,让函数能够确定发生了什么事件,并且执行相应操作。
要阻止特定事件默认行为,可以使用preventDefault()方法。但是只有cancelable属性设置为true的事件,才可以使用preventDefault()来取消默认行为。
stopPropagation()方法用于立即停止事件在DOM层次中的传递,进一步取消事件的捕获或者冒泡。同时,事件对象的eventPhase属性可以确定事件在当前位置。

  • IE中的事件对象

与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。在使用DOM0级方法添加事件处理程序时候,event作为window对象的一个属性存在。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    var event = window.event;
    console.log(event.type);  //"click"
}

此时,在通过window.event取得了event对象,并且检测了被触发事件的类型。可是如果事件处理程序利用的是attachEvent()添加的,那么就会有一个event对象作为参数被传入事件处理程序函数中。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){
    console.log(event.type); //"click"
});

在类似使用这样的方法,可以通过window对象来访问event对象,就像使用DOM0方法一样。
由于事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this会始终等于事件目标。比如

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    console.log(window.event.srcElement === this); //true
}
btn.attachEvent("onclick", function(){
    console.log(event.srcElement === this ); //false
});

第一个事件处理程序中(使用DOM0级方法指定的)srcElement属性等于this,但是第二个事件处理程序中,这二者值并不相同

  • 跨浏览器中的事件对象

IE中event对象的全部信息和方法DOM中都有,只不过实现方式不一样。不过可以基于其相似性,可以写出跨浏览器的事件对象方案。

function getEvent(e) {
    return e || window.event;
}

function getTarget(e) {
    return e.target || e.scrElement;
}

function preventDefault(e) {
    if (e.preventDefault)
        e.preventDefault();
    else
        e.returnValue = false;
}

function stopPropagation(e) {
    if (e.stopPropagation)
        e.stopPropagation();
    else
        e.cancelBubble = true;
}

常用的HTML事件


load:当页面完全加载后在window上触发,当图像加载完成后在img元素上触发,或当嵌入内容加载完成时,在object元素上触发

unload:页面完全卸载后在window上触发,或嵌入内容卸载后在object元素触发

select:用户选择文本框中的字符时触发

change:文本框焦点变化后其值改变时触发

submit:用户提交表单的时候触发

resize:窗口或框架大小变化的时候在window上触发

scroll:用户滚动带滚动条的元素时,在该元素上触发

focus:页面或元素获得焦点时在window及相应元素上触发

blur:页面或元素失去焦点时在window及相应元素上触发

beforeunload:页面卸载前在window上触发

mousewheel:不算HTML的,当用户通过鼠标滚轮与页面交互,在垂直方向滚动页面时触发

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • 事件处理程序在应用中是必不可少的,虽然现在很多框架都有自己实现事件处理方法,但是熟知原生才能让我们应对各种各样的需...
    俗三疯阅读 283评论 0 1
  • JavaScript与HTML之间的交互是通过事件完成的。可以使用事件侦听器来预订事件,正在传统软件工程中被称为观...
    yohn阅读 191评论 1 1
  • 一.事件 事件是用户或浏览器自身执行的某种动作,这是我自己的理解。 二.事件流 事件流描述的是从页面中接收事件的顺...
    卓三阳阅读 2,200评论 1 2
  • 事件流 事件冒泡 IE的事件流叫做事件冒泡,即事件开始时由具体的元素接收,然后逐级向上传播到较为不具体的节点。 事...
    soso101阅读 241评论 0 1
  • 一、事件流 1.1 事件流 事件流:从页面中接受事件的顺序 事件冒泡:即事件开始时由最具体的元素(文档中嵌套层次最...
    范小饭_阅读 1,060评论 1 9