js中DOM事件探究

事件

纲要javascript

  1. 理解事件流html

  2. 使用事件处理程序java

  3. 不一样的事件类型编程

javascript和html的交互是经过事件实现的。事件就是文档或浏览器窗口发生的一些特定交互瞬间。可使用侦听器(事件处理程序)预约事件,以便事件发生时执行相应的代码。segmentfault

1 事件流

浏览器发展到第四代时(IE4和Netscape communicator4)都认为,当你单击某个按钮时,在单击这个按钮的同时,你也单击了包含这个按钮的容器甚至整个页面。就如同你用手指指向一同心圆的圆心,你指向的不单是一个圆,而是以这个圆心为圆心的全部圆。
所谓事件流,指的是页面接收事件时的顺序。IE提出的事件流是事件冒泡流,Netscape communicator提出的事件流是事件捕获流。浏览器

1.1 事件冒泡流编程语言

事件开始时,由最具体的元素(文档中嵌套层次最深的哪一个节点)接收,逐级上传到最不具体的元素(文档)。函数

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
<body>
  <div id='myDiv'>click me</div>
</body>
</html>

上段代码,按照冒泡流的说法,就是你点击div时,沿着DOM树,你也点击了body,html,document。测试

1.2 事件捕获流this

接收事件的顺序,由不太具体的节点先接收,逐级向下传到具体的节点。其用意就是在事件到达目标以前先捕获他。

1.3 DOM事件流

'DOM2级事件',规定事件流包括三个阶段。事件捕获阶段、处于目标阶段、事件冒泡阶段。在事件冒泡阶段对事件作出响应。单击<div>元素会按下图所示顺序触发事件

图片描述

在DOM事件流中,实际的目标(<div>元素)在捕获阶段不会接收到事件,这意味着在捕获阶段,事件从document到<html>再到<body>就中止了。下一个阶段是’处于目标‘阶段,因而事件在<div>上发生,并在事件处理中被当作是冒泡阶段的一部分,而后冒泡阶段发生,事件又传播回文档。

多数支持DOM事件流的浏览器都实现了一种特定的行为:即便"DOM2级事件"规范明确要求捕获阶段不会涉及事件目标,但IE九、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就有两个机会在目标对象上面操做事件。(注意,IE8及更早版本不支持DOM事件流)

  1. 下面这段程序,能够看出在捕获阶段(机会1)和冒泡阶段(机会2)都在目标对象上操做(触发)了事件

  2. 运行这段程序,分别点击红黄绿区域,感觉下DOM事件执行的顺序:(在DOM2级事件处理程序中,绑定在目标元素上的事件执行顺序是按事件的注册前后顺序执行的,因此alert3.2在3.1以前。)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
<body>
    <div id="box" style="background:red;width:200px;height:200px;">
        <div id="outer" style="background:yellow;width:150px;height:150px;">
            <div id="inner" style="background:green;width:70px;height:70px;">click me</div>
        </div>
    </div>
  <script type="text/javascript">
window.onload=function(){
    var Box=document.getElementById('box');
    var outer = document.getElementById("outer");
    var inner = document.getElementById('inner');
    //true为事件捕获,false为事件冒泡
    Box.addEventListener("click",function(){
        alert('1');
    },true)

    Box.addEventListener("click",function(){
        alert('4');
    },false)

    outer.addEventListener("click", function(){
      alert("2");
    }, true);

    inner.addEventListener("click",function(){
        alert('3.2')
    },false);

    inner.addEventListener("click", function(){
      alert("3.1");
     }, true);
    
}
  </script>
</body>
</html>

2 事件处理程序

事件就是用户或浏览器自身执行的某种动做。如click,load,mouseover等都是事件的名字。而响应某个事件的函数叫事件处理程序。事件处理程序的名字以"on"开头,所以click事件的事件处理程序就是onclick。为事件指定处理程序的方式有好几种。

2.1 HTML事件处理程序

某个元素支持的每种事件,均可以使用一个与之相应 事件处理程序 同名的 HTML 特性(属性)来指定。这个特性的值应该是能执行的Javascript代码。例如,要在按钮被单击时执行一些Javascript:<input type="button" value="Click me" onclick="alert('hello world')"/>。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
<body>
  <script type="text/javascript">
    function showMessage(){
      alert('hello world');
    }
  </script>
    <div id="box">
        <input type="button" value="Click me" onclick="showMessage()"/>
    </div>
</body>
</html>

在HTML中指定事件处理程序有两个缺点

  • 缺点一 时间差,HTML加载完成,但js还未加载完成(通常状况,HTML是按注册顺序加载的),若是在showMessage()函数解析前就单击事件,便会引起错误。

解决办法:将HTML事件处理程序封装在一个try-catch块中

<input type="button" value="Click me" onclick="try{showMessage();}catch(ex){}"/>

/番外篇/

try...catch 的做用是测试代码中的错误。

语法:

try
{
   //在此运行代码
   tryStatements
}
catch(err)
{
   //在此处理错误
  catchStatements 
}

参数:

tryStatements 必选项,可能发生错误的代码
catchStatements 可选项,发生tryStatements中关联的错误后执行的代码

示例一,请点击肯定/取消

<html>
<head>
<script type="text/javascript">
var txt=""
function message()
{
try
  {
    //正确是 alert,错误代码即 adddlert
  adddlert("Welcome guest!")
  }
catch(err)
  {
  txt="There was an error on this page.\n\n"
  txt+="Click OK to continue viewing this page,\n"
  txt+="or Cancel to return to the home page.\n\n"
  //confirm(message)---要在 window 上弹出的对话框中显示的纯文本(而非 HTML 文本)
  if(!confirm(txt))
    {
    document.location.href="https://segmentfault.com/u/xiakejie"
    }
  }
}
</script>
</head>
<body>
<input type="button" value="View message" onclick="try{message();}catch(err){}" />
</body>
</html>

语法:

try  {  
   tryStatements}  
catch(exception){  
   catchStatements}  
finally  {  
   finallyStatements}

参数:
finallyStatements:可选项,当tryStatements,catchStatements皆抛出错误执行的代码

示例二 与稍后讲的跨浏览器处理程序比较,与if...elaeif...else比较(对这里的方法若不理解,后面会讲到)

<html>
<head>
</head>
<body>
<input id='b1' type='button' value='按钮'/>
<script>
(function(){
  var oBtn=document.getElementById("b1");
  function mto(){
  alert("123");
  };

  try 
  {
  oBtn.attachEvent("onclick",mto);
  }
  catch(e)
  {
  oBtn.addEventListener("click",mto,false);
  }
  finally //DOM0级事件处理程序  
  {  
  oBtn.onclick=mto;
   }

 })();
</script>
</body>
</html>
  • 缺点二 HTML与javascript代码紧密耦合,若是要更换事件处理程序,HTML部分和javascript部分都要修改。(另一个缺点会在后续另篇文章解析)

2.2 DOM0级事件处理程序

经过Javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法,在第四代WEB浏览器出现,至今全部的浏览器都支持。要使用javascript指定事件处理程序,首先必须取得一个要操做的对象的引用

每一个元素(包括window document)都有本身的事件处理程序属性,这些属性一般所有小写,例如onclick。以下

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
<body>
  <div id="box">
      <input type="button" value="Click me" id="btn" />
  </div>
  <script type="text/javascript">
    function showMessage(){
      alert('DOM0级事件处理程序');
    }
    var oBtn=document.getElementById('btn');
    oBtn.onclick=showMessage;
  </script>
</body>
</html>

在此咱们经过一个文档对象取得一个按钮的引用,而后为他指定了onclick事件处理程序,但要注意在这些代码(好比这里的showmessage函数代码)运行之前不会指定事件处理程序(指的是当你点击按钮时,不会执行showmessage函数代码,由于这些代码有可能尚未运行),所以若是这些代码在页面中位于按钮后面,有可能在一段时间内怎么点击都没有反应。

2.2.1 番外篇

不少同窗或许纳闷 oBtn.onclick=showMessage;oBtn.onclick=showMessage(); 区别

showMessage是一个函数名,ECMAscript中函数是一个对象,函数名则是一个指向函数对象的指针,使用不带括号的函数名是访问函数指针,而不是调用函数。若要调用则带括号。

我是这样理解的 var funName=function(){...}; 是一个函数表达式,这个函数表达式是将一个匿名函数赋值给一个变量。在JS是面向对象编程语言,变量也是对象。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
<body>
  <script type="text/javascript">
    var fnName=function(){
    alert('Hello World');
    }();
  </script>
</body>
</html>

上例咱们看出函数表达式后加()直接调用。这样理解 var funName=function(){...}; 即是一个函数表达式,oBtn.onclick=showMessage(); 这样即是把他当作一个函数表达式,而且直接执行这个函数,而不是DOM0级事件处理程序。这块知识我在另外一篇文章中有详细讲解 https://segmentfault.com/a/11...

2.2.2 DOM0级事件处理程序的做用域

使用DOM0级方法指定事件处理程序被认为是元素的方法(之因此称元素的方法,是由于使用DOM0级事件处理程序必须首先得到一个操做对象,这个操做对象也就是DOM元素)。所以这时事件处理程序是在元素的做用域中进行。那么,程序中的this引用当前元素。在事件处理程序中能够经过this访问元素的任何属性和方法。DOM0级事件处理程序会在事件流的冒泡阶段被处理。

var oBtn=document.getElementById('btn');
oBtn.onclick=function(){
    alert(this.id)
};

2.2.3 删除DOM0级事件处理程序

oBtn.onclick=null;

2.3 DOM2级事件处理程序

DOM2级事件处理程序定义了两个方法:用于处理指定和删除事件处理程序的操做,addEventListener()和removeEventListener()。全部的DOM节点都包含这两个方法。都接收三个参数:要处理的事件名,做为事件处理程序的函数和布尔值。true表示在捕获阶段调用事件处理程序,false表示在冒泡阶段调用事件处理程序。使用DOM2级方法指定事件处理程序也被认为是元素的方法,事件处理程序也是在元素的做用域中运行的,也能够经过this访问元素的任何属性和方法。

2.3.1 DOM2级事件处理程序优势

能够添加多个事件处理程序。而且按添加的顺序触发。

var oBtn=docunment.getElementById('myBtn');
oBtn.addEventListener('click',function(){alert(this.id)},false);
oBtn.addEventListener('click',function(){alert('hello')},false);

2.3.1 DOM2级事件处理程序的移除

经过addEventListener()添加的事件处理程序只能用removeEventListener()移除,而且传入参数要与添加处理程序参数彻底相同。意味着经过abbEventListener()添加的匿名函数没法被移除

为了兼容各类浏览器咱们大多数将事件处理程序添加到事件流的冒泡阶段。除非为了在事件到达目标前捕获他,才将事件处理程序添加到捕获阶段。

2.3.2 支持的浏览器

IE9,Firefox,Safari,Chrome和Opers支持DOM2级事件处理程序。

2.4 IE事件处理程序

IE事件处理程序,指定和删除事件处理程序方法:attachEvent()和detachEvent().接受相同的两个参数,事件处理程序名称与事件处理程序函数,(注意DOM2级第一个参数是事件名)。经过attachEvent()添加的事件处理程序都会被加到冒泡阶段,是由于IE8及更早版本只支持冒泡事件流

使用attachEvent()为按钮添加一个事件处理程序.

var bnt = document.getElementById('bnt');
btn.attachEvent('onclick',function(){alert("hello");});

IE事件处理程序与DOM0级事件处理程序主要区别:事件处理程序的做用域。使用DOM0级方法,事件处理程序是在其所属元素内运行;attachElent()方法时,事件处理程序是在全局做用域中运行,this值所以等于Window. 在编写跨浏览器代码时牢记这一区别(跨浏览器稍后会说到)

同DOM2级事件处理程序同样,移除事件只能经过detachEvent()移除,而且传入的参数要同样。

2.4.1支持的浏览器

IE和Opera

2.5跨浏览器事件处理程序

回顾一下前面的几种事件处理程序。HTML事件处理程序,DOM0级事件处理程序,DOM2级事件处理程序,IE事件处理程序。

HTML事件处理程序,适合全部浏览器;DOM0级,适合全部浏览器;DOM2级,适合IE9,Firefox,Safari,Chrome和Opers;IE事件处理程序,适合IE和Opera。

2.5.1 跨浏览器事件处理程序

为了以跨浏览器方式处理事件,方法一,使用可以隔离浏览器差别的javascript库。方法二,本身开发最适合的处理方法。咱们这里使用的即是方法二。运用能力检测思想。咱们要保证处理事件的代码在大多数浏览器下一致的运行,只需关注冒泡阶段(由于全部现代浏览器都支持事件冒泡)。

在讲跨浏览器事件处理程序以前,咱们先温习一下Object。建立Object实例的方式有两种

  • 使用NEW操做符后跟Object实例,var person = new Object(); person.name="liMing"; person.age = 29;

  • 第二种是使用对象字面量表示法,var person = {name:"liMing",age:29};

访问对象属性方法也有两种

  • 点表示法。alert(person.name);

  • 第二种是是方括号表示法,alert(person['name']);//优势,能够经过变量来访问属性

建立一个对象eventUtil,对象包含两个方法,一个方法给元素添加事件addHeadler(),另外一个方法给元素去除事件removeHeader()。方法接受三个参数,要操做的元素,事件名称,和事件处理程序函数。

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.addEventListener(type,handler,false);
    }else if (element.attachEvent) {
      element.attachEvent('on'+type,handler);
    }else {
      element['on'+type]=null;
    }
  } 
};

引用:EventUnit.addHandler(element,'click',handler);

简析:能够将EventUnil当作使用对象字面量法建立的Object实例。addHandler,removeHandler当作对象的属性。在ECMAScript中函数名自己就是变量,因此函数也能够做为值来使用,因此能够讲function(evement,type,handler){}看做是属性的值。由于ECMAScript中函数能够传进来多个参数,参数的数据类型不限制,因此这里能够传入对象eventment,字符串type,函数/对象handler.

引用形式的简析:
建立一个对象:var person = {name:'liMing'}; 也能够var person=new Object(); person.name='liMing';
那么EventUnil.addHandler(element,'click',handler)能够理解为var EventUnil.addHandler=function(){};这一函数表达式的调用。注,以上简析均是我的理解,若不正确,但愿指正。

3 事件对象

在触发DOM上的某个事件时,会产生一个事件对象Event,这个对象包含着全部与事件有关的信息。全部的浏览器都支持Event对象,但支持的方式不一样。

3.1 DOM中的事件对象

不管指定事件处理程序时使用什么方法(DOM0,DOM2),都会传入Event对象。event对象包含与建立它的特定事件有关的属性和方法,触发的事件类型不同,可用的属性和方法也不同,不过,全部事件都会有下表列出的成员

图片描述
图片描述

3.1.1 currentTarget 与 target

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。事件处理程序存在于按钮的父节点中(例如document.body)

document.body.onclick=function(event){
  alert(event.currentTarget===document.body);//true
  alert(this===document.body);//true
  alert(event.target===document.getElementById('myBtn')); //true
}

该结果是因为按钮上没有注册事件处理程序,点击按钮时,click事件冒泡到了document.body,在那里事件才获得处理。

3.1.2 type

在须要一个函数处理多个事件时,可以使用type属性

var btn = document.getElementById('myBtn');
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.click = handler;
btn.mouseover = handler;
btn.mouseout = handler;

3.1.3 阻止事件的默认行为 preventDefault()

取消特定事件的默认行为。例如,连接的默认行为就是在被单击时会导航到href特性指定的URL,若是你想阻止连接导航这一默认行为,经过连接的onclick事件处理程序取消它。

var link = document.getElementById('myLink');
link.onclick = function(event){
    event.preventDefault();
};

3.1.4 取消进一步事件的捕获或冒泡 stopPropagation()

在讲currentTarget与target时,咱们知道按钮没有注册事件处理程序,click事件冒泡到了document,body上。如今咱们能够阻止冒泡行为了,结果避免触发注册在document.body上的事件处理程序

var btn = document.getElementById('myBtn');
  btn.onclick = function(event){
    event.stopPropagation();
  };
  document.body.onclick = function(event){
    alert(event.currentTarget===document.body);
    alert(this===document.body);
    alert(event.target===btn);
  };

3.1.5 肯定事件位于当前事件流那个阶段eventPhase

注意,只有在事件处理程序执行期间,event对象才会存在,一旦事件处理程序执行完毕,event对象便会被销毁。

3.2 IE中的事件对象

要访问IE中的event对象有几种不一样的方式,访问的方式取决于指定事件处理程序的方法。

  • 使用DOM0级方法添加事件处理程序。event对象做为window对象的一个属性存在。

var btn = document,getElementById('myBtn');
btn.onclick = function(){
    var event = window.event;
    alert(event.type); //"click"
};
  • 使用IE方法添加事件处理程序,event对象做为参数传入事件处理程序中。

var btn = document,getElementById('myBtn');
btn.attachEvent ("onclick",function(event){
  alert(event.type);//"click"
});
  • 经过HTML特性指定事件处理程序,经过一个名为event变量访问event对象(与DOM中的事件模型相同)

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

3.2.1 IE中event对象的属性和方法

其中不少属性和方法都有对应的或相关的DOM属性和方法,与DOM的event对象同样,这些属性和方法也会由于事件类型的不一样而不一样,但全部事件对象都会包含下表所列的属性和方法
图片描述

3.2.2 事件目标
由于事件处理程序做用域是根据指定他的方式肯定的,因此不能认为this始终等于事件目标。因为个人浏览器版本问题,书中的现象复现不了,因此用下程序代替。

var btn = document.getElementById('myBtn');
  function showMes(event){
    var event = window.event||event;
    var ele = event.target||event.srcElement;
    alert(ele === this);
  };
  var eventUnit = {
    addHandler: function(element,type,handler){
      if (element.attachEvent) {
        element.attachEvent(type,handler)
      }else{
        element[type]=handler;
      }
    }
  };
  eventUnit.addHandler(btn,'onclick',showMes);

3.2.3 取消事件的默认行为,returnValue

returnValue属性至关与DOM中的preventDefault()方法,只要将returnValue设置为falsae就能够阻止事件的默认行为。一样由于浏览器版本问题,书中代码修改以下:

var link = document.getElementById('myLink');
  function stopEvent(event){
    var event = event||window.event;
    event.returnValue = false;
    event.preventDefault();
  };
  link.onclick = stopEvent;

3.2.4 阻止事件冒泡 cancelBubble

cancelBubble属性与DOM中的stopPropagatioin()方法做用相同,IE不支持事件捕获,因此只用取消事件冒泡。事件处理程序中将cancelBubble设置为true即可以阻止事件冒泡行为。

3.3 跨浏览器的事件对象

<body>
  <input type="button" name="button" id="myBtn" value="button">
  <a href="http://www.w3school.com.cn" id="myLink">W3School</a>
  <script type="text/javascript">
  var btn = document.getElementById('myBtn');
  var link = document.getElementById('myLink');
  var EventUtil = {
    getEvent : function(event){
      return event?event:window.event;
    },
    getTarget : function(event){
      return event.target||event.srcElement;
     //return event.target?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;
      }
    },
  };
  btn.onclick = function(event){
    event = EventUtil.getEvent(event);
  };
  link.onclick = function(event){
    event = EventUtil.getEvent(event);
    EventUtil.preventDefault(event);
    EventUtil.stopPropagation(event);
  };
  btn.onclick = function (event){
    event=EventUtil.getEvent(event);
    target = EventUtil.getTarget(event);
    alert('stopPropagation');
    alert(target);
    EventUtil.stopPropagation(event);
  };
  document.body.onclick = function(event){
    alert('body clicked');
  };
  </script>
</body>

本文参考javascript高级程序设计第三版

相关文章
相关标签/搜索