>  기사  >  웹 프론트엔드  >  javascript_javascript 스킬의 이벤트 처리에 대한 자세한 설명

javascript_javascript 스킬의 이벤트 처리에 대한 자세한 설명

WBOY
WBOY원래의
2016-05-16 15:33:241236검색

1. 이벤트 전파 메커니즘

클라이언트 측 JavaScript 프로그램(즉, 브라우저)은 비동기 이벤트 중심 프로그래밍 모델을 채택합니다. 웹 브라우저는 문서, 브라우저, 요소 또는 이와 관련된 개체에 흥미로운 일이 발생하면 이벤트를 생성합니다. JavaScript 애플리케이션이 특정 유형의 이벤트에 관심이 있는 경우 해당 이벤트가 발생할 때 호출될 하나 이상의 함수를 등록할 수 있습니다. 물론 이 스타일은 웹 프로그래밍에만 국한된 것이 아니며 그래픽 사용자 인터페이스를 사용하는 모든 응용 프로그램이 이를 채택합니다.

이벤트 처리를 자세히 설명하고 싶으므로 몇 가지 기본 개념부터 시작하겠습니다.

 ①이벤트 유형: 은 어떤 유형의 이벤트가 발생했는지 설명하는 문자열입니다. 예를 들어, "mousemove"는 사용자가 마우스를 움직였다는 의미이고, "keydown"은 키보드의 키가 눌렸다는 의미입니다. 이벤트 유형은 이벤트 이름이라고도 하는 문자열입니다.

  ②이벤트 대상 : 은 이벤트가 발생하거나 이와 관련된 객체입니다. Window, Document 및 Element 개체는 가장 일반적인 이벤트 대상입니다. 물론 AJAX의 XMLHttpRequest 객체도 이벤트 대상입니다.

  ③이벤트 핸들러: 는 이벤트를 처리하거나 이에 응답하는 함수입니다. 애플리케이션은 이벤트 유형과 이벤트 대상을 지정하여 웹 브라우저에 이벤트 핸들러를 등록합니다.

  ④이벤트 객체: 는 특정 이벤트와 관련된 객체로, 해당 이벤트에 대한 자세한 정보를 담고 있습니다. 이벤트 객체는 이벤트 처리 함수에 매개변수로 전달됩니다(그러나 IE8 및 이전 버전에서는 전역 변수인 event가 이벤트 객체입니다). 이벤트 객체에는 이벤트 유형을 지정하는 type 속성과 이벤트 대상을 지정하는 target 속성이 있습니다(그러나 IE8 및 이전 버전에서는 target 대신 srcElement가 사용됩니다). 물론 다양한 유형의 이벤트는 관련 이벤트 개체에 대한 다른 고유한 속성도 정의합니다. 예를 들어, 마우스 이벤트에 대한 관련 개체에는 마우스 포인터의 좌표가 포함되고, 키보드 이벤트에 대한 관련 개체에는 누른 키와 수정자 키에 대한 세부 정보가 포함됩니다.

이상으로 4가지 기본 개념이 완성되었습니다. 따라서 질문이 생깁니다. 웹 페이지에서 요소 a의 하위 요소 b를 마우스로 클릭하면 하위 요소 b에 등록된 이벤트 핸들러가 먼저 실행되어야 하는지, 아니면 요소 a에 등록된 이벤트 핸들러가 실행되어야 하는지에 대한 질문이 있습니다. 먼저(요소 a와 해당 하위 요소 b 모두 등록된 이벤트 핸들러를 가지고 있다고 가정)? 독자로서 이 질문에 대해 생각해 본 적이 있나요?

이 문제는 브라우저의 이벤트 전파 메커니즘과 관련이 있습니다. 이벤트 버블과 이벤트 캡쳐라는 말은 다들 들어보셨을 거라 믿습니다! 예, 브라우저의 이벤트 전파 메커니즘입니다. 사진도 없고, 진실도 없고, 첨부된 사진도 없나요? 그렇다면 어떻게 관대할 수 있습니까?

그림을 읽으신 후에는 브라우저의 이벤트 전파 메커니즘을 대략적으로 이해하셨으리라 믿습니다. 이벤트가 발생하면 먼저 브라우저의 최상위 개체 창에서 해당 창으로 전달됩니다. 이벤트를 트리거했습니다. 요소는 이벤트 캡처 프로세스입니다. 그러나 아직 모든 것이 끝난 것은 아닙니다. 이벤트는 이 요소에서 이벤트 버블링 프로세스인 Window 객체까지 전달됩니다. 그러나 IE8 및 이전 버전에서는 이벤트 모델이 캡처 프로세스를 정의하지 않고 캡처 프로세스만 정의합니다. 버블링 과정).

그래서 위의 질문에 대해서는 요소 a에 의해 등록된 이벤트 핸들러가 캡처 프로세스에 있는지 아니면 버블링 프로세스에 있는지에 따라 다릅니다. 그렇다면 캡처 프로세스에서 이벤트 핸들러를 등록하는 것은 정확히 무엇이며 버블링 프로세스에서 이벤트 핸들러를 등록하는 방법은 무엇입니까? 이제 이벤트 핸들러를 등록하는 여러 가지 방법에 대해 이야기해 보겠습니다.

1. HTML 태그 속성을 이벤트 핸들러로 설정

문서 요소의 이벤트 핸들러 속성의 이름은 "on"과 이벤트 이름으로 구성됩니다(예: onclick, onmouseover). 물론 이 양식은 DOM 요소에 대한 이벤트 핸들러만 등록할 수 있습니다. 예:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
 #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
 #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
 </style>
</head>
<body>
 <div id="div1" onClick="console.log('div1');">div1
 <div id="div2" oNClick="console.log('div2');">div2
 <div id="div3" onclick="console.log('div3');" onclick="console.log('div3333');">div3
 </div>
 </div>
 </div>
<script type="text/javascript">
</script>
</body>
</html>

结果(鼠标点击div3区域后):

从结果中可以看出:

  ①因为HTML里面不区分大小写,所以这里事件处理程序属性名大写、小写、大小混写均可,属性值就是相应事件处理程序的JavaScript代码;

  ②若给同一元素写多个onclick事件处理属性,浏览器只执行第一个onclick里面的代码,后面的会被忽略;

  ③这种形式是在事件冒泡过程中注册事件处理程序的;

2.设置JavaScript对象属性为事件处理程序

  可以通过设置某一事件目标的事件处理程序属性来为其注册相应的事件处理程序。事件处理程序属性名字由“on”后面跟着事件名组成,例如:onclick、onmouseover。实例:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
 #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
 #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
 </style>
</head>
<body>
 <div id="div1">div1
 <div id="div2">div2
 <div id="div3">div3
 </div>
 </div>
 </div>
<script type="text/javascript">
 var div1 = document.getElementById('div1');
 var div2 = document.getElementById('div2');
 var div3 = document.getElementById('div3');
  div1.onclick = function(){
    console.log('div1');
  };
  div2.onclick = function(){
    console.log('div2');
  };
  div3.onclick = function(){
    console.log('div3');
  };
  div1.onclick = function(){
    console.log('div11111');
  };

  div1.onClick = function(){
    console.log('DIV11111');
  };

</script>
</body>
</html>

结果(鼠标点击div3区域后):

 从结果中可以看出:

  ①因为JavaScript是严格区分大小写的,所以,这种形式下属性名只能按规定小写;

  ②若给同一元素对象写多个onclick事件处理属性,后面写的会覆盖前面的(ps:这就是在修改一个对象属性的值,属性的值是唯一确定的);

  ③这种形式也是在事件冒泡过程中注册事件处理程序的;

3.addEventListener()

  前两种方式出现在Web初期,众多浏览器都有实现。而addEventListener()方法是标准事件模型中定义的。任何能成为事件目标的对象——这些对象包括Window对象、Document对象和所有文档元素等——都定义了一个名叫addEventListener()的方法,使用这个方法可以为事件目标注册事件处理程序。addEventListener()接受三个参数:第一个参数是要注册处理程序的事件类型,其值是字符串,但并不包括前缀“on”;第二个参数是指当指定类型的事件发生时应该调用的函数;第三个参数是布尔值,其可以忽略(某些旧的浏览器上不能忽略这个参数),默认值为false。这种情况是在事件冒泡过程中注册事件处理程序。当其为true时,就是在事件捕获过程中注册事件处理程序。实例:

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>test</title>
  <style type="text/css">
    #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
    #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
    #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
  </style>
</head>
<body>
  <div id="div1">div1
    <div id="div2">div2
      <div id="div3">div3
      </div>
    </div>
  </div>
<script type="text/javascript">
  var div1 = document.getElementById('div1');
  var div2 = document.getElementById('div2');
  var div3 = document.getElementById('div3');
  div1.addEventListener('click', function(){ console.log('div1-bubble'); }, false);
  div2.addEventListener('click', function(){ console.log('div2-bubble'); }, false);
  div3.addEventListener('click', function(){ console.log('div3-bubble'); }, false);
  div3.addEventListener('click', function(){ console.log('div3-bubble222'); }, false);
  div1.addEventListener('click', function(){ console.log('div1-capturing'); }, true);
  div2.addEventListener('click', function(){ console.log('div2-capturing'); }, true);
  div3.addEventListener('click', function(){ console.log('div3-capturing'); }, true);
</script>
</body>
</html>

结果(鼠标点击div3区域后):

从结果中可以看出:

  ①addEventListener()第三个参数的作用正如上面所说;

  ②通过addEventListener()方法给同一对象注册多个同类型的事件,并不会发生忽略或覆盖,而是会按顺序依次执行;

相对addEventListener()的是removeEventListener()方法,它同样有三个参数,前两个参数自然跟addEventListener()的意义一样,而第三个参数也只需跟相应的addEventListener()的第三个参数保持一致即可,同样可以省略,默认值为false。它表示从对象中删除某个事件处理函数。实例:

div1.addEventListener('click', div1BubbleFun, false);
div1.removeEventListener('click', div1BubbleFun, false);
function div1BubbleFun(){
 console.log('div1-bubble');
}

4.attachEvent()

  但是,IE8以及其之前版本的浏览器并不支持addEventListener()和removeEventListener()。相应的,IE定义了类似的方法attachEvent()和detachEvent()。因为IE8以及其之前版本浏览器也不支持事件捕获,所以attachEvent()并不能注册捕获过程中的事件处理函数,因此attachEvent()和detachEvent()要求只有两个参数:事件类型和事件处理函数。而且,它们的第一个参数使用了带“on”前缀的事件处理程序属性名。实例:

var div1 = document.getElementById('div1');
div1.attachEvent('onclick', div1BubbleFun);
function div1BubbleFun(){
  console.log('div1-bubble');
}

  相应的,从对象上删除事件处理程序函数使用detachEvent()。例如:

div1.detachEvent('onclick', div1BubbleFun);

  到此为止,我们已经说了浏览器中事件传播机制以及各种注册事件处理程序的方法。下面我们就再说说事件处理程序调用时的一些问题吧!

二.事件处理程序的调用

1.事件处理程序的参数:正如前面所说,通常事件对象作为参数传递给事件处理函数,但IE8以及其之前版本的浏览器中全局变量event才是事件对象。所以,我们在写相关代码时应该注意兼容性问题。实例(给页面上id为div1的元素添加点击事件,当点击该元素时在控制台输出事件类型和被点击元素本身):

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 if(div1.addEventListener){
 div1.addEventListener('click', div1Fun, false);
 }else if(div1.attachEvent){
 div1.attachEvent('onclick', div1Fun);
 }
 function div1Fun(event){
 event = event || window.event;
 var target = event.target || event.srcElement;
 console.log(event.type);
 console.log(target);
 }
 </script>
</body>
</html>

2.事件处理程序的运行环境:关于事件处理程序的运行环境,也就是在事件处理程序中调用上下文(this值)的指向问题,可以看下面四个实例。

实例一:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 </script>
</body>
</html>

  结果一:

  从结果可以看出:

    ①第一种方法事件处理程序中this指向这个元素本身;

实例二:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 div1.onclick = function(){
 console.log('div1.onclick:');
 console.log(this);
 };
 </script>
</body>
</html>

  结果二:

  从结果可以看出:

    ①第二种方法事件处理程序中this也指向这个元素本身;

    ②存在第二种方法时,它会覆盖第一种方法注册的事件处理程序;

实例三:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 div1.onclick = function(){
 console.log('div1.onclick:');
 console.log(this);
 };
 div1.addEventListener('click', function(){
 console.log('div1.addEventListener:');
 console.log(this);
 }, false);
 </script>
</body>
</html>

  结果三:

  从结果可以看出:

    ①第三种方法事件处理程序中this也指向这个元素本身;

    ②第三种方法并不会覆盖第一种或第二种方法注册的事件处理程序;

实例四:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow: hidden;}
 </style>
</head>
<body>
 <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 div1.onclick = function(){
 console.log('div1.onclick:');
 console.log(this);
 };
 div1.attachEvent('onclick', function(){
 console.log('div1.attachEvent:');
 console.log(this === window);
 });
 
 </script>
</body>
</html>

  结果四:

  从结果可以看出:

    ①第四种方法事件处理程序中this指向全局对象Window;

    ②第四种方法也不会覆盖第一种或第二种方法注册的事件处理程序;

3.事件处理程序的调用顺序:多个事件处理程序调用规则如下:

  ①通过HTML属性注册的处理程序和通过设置对象属性的处理程序一直优先调用;

  ②使用addEventListener()注册的处理程序按照它们的注册顺序依次调用;

  ③使用attachEvent()注册的处理程序可能按照任何顺序调用,所以代码不应该依赖于调用顺序;

4.事件取消:

①取消事件的浏览器默认操作(比如点击超链接元素会自动发生页面跳转的默认操作):如果使用前两种方法注册事件处理程序,可以在处理程序中添加返回值false来取消事件的浏览器默认操作。在支持addEventListener()的浏览器中,也可以通过调用事件对象的preventDefault()方法取消事件的默认操作。至于IE8及其之前的浏览器可以通过设置事件对象的returnValue属性为false来取消事件的默认操作。参考代码:

function cancelHandler(event){
 var event = event || window.event;
 if(event.preventDefault){
 event.preventDefault();
 }
 if(event.returnValue){
 event.returnValue = false;
 }
 return false;
}

②取消事件传播:在支持addEventListener()的浏览器中,可以调用事件对象的一个stopPropagation()方法阻止事件的继续传播,它能工作在事件传播期间的任何阶段(捕获期阶段、事件目标本身、冒泡阶段);但是在IE8以及其之前版本的浏览器中并不支持stopPropagation()方法,而且这些浏览器也不支持事件传播的捕获阶段,相应的,IE事件对象有一个cancelBubble属性,设置这个属性为true能阻止事件进一步传播(即阻止其冒泡)。参考代码(阻止发生在div3区域的点击事件冒泡到div2和div1):

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <title>test</title>
 <style type="text/css">
 #div1{width: 300px; height: 300px; background: red; overflow:hidden;}
 #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;}
 #div3{margin:50px auto; width: 100px; height: 100px; background: blue;}
 </style>
</head>
<body>
 <div id="div1">div1
 <div id="div2">div2
 <div id="div3">div3
 </div>
 </div>
 </div>
 <script type="text/javascript">
 var div1 = document.getElementById('div1');
 var div2 = document.getElementById('div2');
 var div3 = document.getElementById('div3');
    div1.onclick = function(){
 console.log('div1');
    };
    div2.onclick = function(){
 console.log('div2');
    };
    div3.onclick = function(event){
 stopEventPropagation(event);
 console.log('div3');
    };
 
 function stopEventPropagation(event){
 var event = event || window.event;
 if(event.stopPropagation){
 event.stopPropagation();
 }else{
 event.cancelBubble = true;
 }
 }
 </script>
</body>
</html>

위는 JavaScript 이벤트 처리에 대한 전체 소개입니다. 물론 이벤트 버블링에 대해 아직 활용해야 할 사항이 있습니다. 이는 우리가 종종 이벤트 프록시 또는 이벤트 위임이라고 부르는 것입니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.