>웹 프론트엔드 >JS 튜토리얼 >js의 DOM 이벤트 흐름에 대한 자세한 설명

js의 DOM 이벤트 흐름에 대한 자세한 설명

小云云
小云云원래의
2018-03-20 17:46:301830검색

이벤트는 사용자나 브라우저 자체에서 수행되는 작업입니다. 클릭, 로드, 마우스 오버 등이 있습니다. 이벤트 흐름은 페이지에서 이벤트가 수신되는 순서를 설명하며, 페이지에서 이벤트가 전파되는 순서로도 이해할 수 있습니다. 이벤트 핸들러 이벤트에 응답하는 함수를 이벤트 핸들러(또는 이벤트 리스너)라고 합니다.

DOM0 레벨 이벤트 핸들러

다음 예에서는 dom0에서 이벤트를 바인딩하는 두 가지 방법과 이벤트 바인딩을 해제하는 방법을 보여줍니다. 코드를 실행할 때 두 이벤트가 동시에 동일한 요소에 바인딩되면 이벤트가 바인딩됩니다. 나중에는 이전에 바인딩된 이벤트를 덮어씁니다.

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
       <button type="button" id="btn" onclick="alert(&#39;btn1&#39;)">点击按钮</button>
</body>
<script>    var btn=document.getElementById("btn");    //绑定事件    btn.onclick=function(){        alert("btn");
   };    //事件解绑    btn.onclick=null;
</script>
</html>

dom 레벨 2 이벤트 핸들러

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <meta name="viewport"          content="width=device-width, initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0"/>
   <title>Title</title>
</head>
<body>
<button type="button" id="btn" onclick="alert(&#39;btn1&#39;)">点击按钮</button>
</body>
<script>    var btn=document.getElementById("btn");    //绑定事件    btn.addEventListener("click",function(){         alert("btn");
   },false);    //事件解绑   btn.removeEventListener("click",function(){       alert("解绑");
  })
</script>
</html>

참고: addEventListener() 메서드의 세 번째 매개변수의 기본값은 false입니다. 이는 프로그램이 이벤트 버블링 형태로 실행된다는 의미입니다. 이벤트 캡쳐 형태로. IE가 아닌 브라우저로 제한됩니다. 즉, 브라우저는 이벤트를 바인딩하기 위해 attachmentEventListener를 사용합니다. IE 브라우저와 IE가 아닌 브라우저 간의 이벤트 바인딩 및 바인딩 해제와 관련하여 둘 사이의 차이점을 보여주는 예제를 작성하겠습니다.

var eventUtil = {    //添加事件    addEventHandle:function(element,eventType,fn){        if(element.addEventListener){//非IE            element.addEventListener(eventType,fn,false);
       }else if(element.attachEvent){//IE            element.attachEvent(&#39;on&#39;+eventType,fn);//这里拼接上&#39;on&#39;,调运的时候不要加on,使用click等。        }else{//不支持DOM2级,使用DOM0级方式            element[&#39;on&#39;+eventType] = fn;//这里使用[]方式实现对象的属性添加,相当于.的作用        }
   },    //删除事件    removeEventHandle:function(element,eventType,fn){        if(element.removeEventListener){//非IE,不带&#39;on&#39;            element.removeEventListener(eventType,fn,false);
       }else if(element.detachEvent){//IE,带&#39;on&#39;            element.detachEvent(&#39;on&#39;+eventType,fn);
       }else{//不支持DOM2级,使用DOM0级方式            element[&#39;on&#39;+eventType] = fn;
       }
   },    //获取事件对象    getEvent:function(event){        return event?event:window.event;
   },    //获取事件类型    getType:function(event){        return event.type;
   },    //获取执行事件的目标元素    getTarget:function(event){        return event.target||event.srcElement;
   },    //禁用默认行为    preventDefault:function(event){        if(event.preventDefault){            event.preventDefault();//非IE        }else{            event.returnValue = false;//针对IE        }

   },    //阻止传播冒泡    stopPropagation:function(event){        if(event.stopPrapagation){            event.stopPropagation();//非IE        }else{            event.cancelBubble = true;//针对IE        }
   }
}

dom3事件处理程序

 DOM浏览器中可能发生的事件有很多种,不同事件类型具有不同的信息,DOM3级事件规定了一下几种事件:
    UI事件,当用户与页面上的元素交互时触发;
    焦点事件,当元素获得或者失去焦点时触发;
    鼠标事件,当用户通过鼠标在页面上执行操作时触发;
    滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
    文本事件,当在文档中,输入文本时触发;
    键盘事件,当用户通过键盘在页面上执行操作时触发;
    合成事件,当为IME(Input Method Editor,输入法编辑器)输入字符时触发;
    变动事件,当底层Dom结构发生变化时触发;
    DOM3级事件模块在DOM2级事件的基础上重新定义了这些事件,也添加了一些新事件。包括IE9在内的主流浏览器都支持DOM2级事件,IE9也支持DOM3级事件。
DOM中的事件模拟(自定义事件):
DOM3级还定义了自定义事件,自定义事件不是由DOM原生触发的,它的目的是让开发人员创建自己的事件。要创建的自定义事件可以由createEvent("CustomEvent");
返回的对象有一个initCustomEvent()方法接收如下四个参数。
1)type:字符串,触发的事件类型,自定义。例如 “keyDown”,“selectedChange”;
2)bubble(布尔值):标示事件是否应该冒泡;
3)cancelable(布尔值):标示事件是否可以取消;
4)detail(对象):任意值,保存在event对象的detail属性中;
可以像分配其他事件一样在DOM中分派创建的自定义事件对象。如:

   
var  p = document.getElementById("myp");
EventUtil.addEventHandler(p,"myEvent", function () {
alert("p myEvent!");
});
EventUtil.addEventHandler(document,"myEvent",function(){
alert("document myEvent!");
});
if(document.implementation.hasFeature("CustomEvents","3.0")){
var e = document.createEvent("CustomEvent");
e.initCustomEvent("myEvent",true,false,"hello world!");
p.dispatchEvent(e);
}

这个例子中创建了一个冒泡事件“myEvent”。而event.detail的值被设置成了一个简单的字符串,然后在p和document上侦听该事件,因为在initCustomEvent中设置了事件冒泡。所以当p激发该事件时,浏览器会将该事件冒泡到document。
IE中的事件模拟(IE8及之前版本中):
与DOM中事件模拟的思路类似,先创建event对象,再为其指定相应信息,然后再使用该对象来触发事件。当然IE在实现以下每个步骤都不太一样。
例如:

var btn = document.getElementById("myBtn");
//创建事件对象,不接受任何参数,结果会返回一个通用的event对象,你必须为该event对象指定所有必要的信息。
var event  = document.createEventObject();
//初始化事件对象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY =0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;

//触发事件
btn.fireEvent("onclick",event);

事件流的分类

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

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

  • dom事件流:DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段==>处于目标阶段==>事件冒泡阶段。首先发生的是事件捕获阶段,为截获事件提供了机会。然后是实际的目标接收事件。最后一个阶段是冒泡阶段。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
       <p id="node1">
           <p id="node2">
               <p id="node3">
                   <p id="node4">click me</p>
               </p>
           </p>
       </p>
</body>
<script>    var node1=document.getElementById("node1");    var node2=document.getElementById("node2");    var node3=document.getElementById("node3");    var node4=document.getElementById("node4");    node1.addEventListener("click",function(){        alert("node1");
    },false);    node2.addEventListener("click",function(){        alert("node2");
    },false);    node3.addEventListener("click",function(){        alert("node3");
    },false);    node4.addEventListener("click",function(){        alert("node4");
    },false);
</script>
</html>

以上代码分别给node1-node4个节点绑定事件,且采用事件冒泡的形式执行,从而分别会弹出node4->node3->node3->node1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
       <p id="node1">
           <p id="node2">
               <p id="node3">
                   <p id="node4">click me</p>
               </p>
           </p>
       </p>
</body>
<script>    var node1=document.getElementById("node1");    var node2=document.getElementById("node2");    var node3=document.getElementById("node3");    var node4=document.getElementById("node4");    node1.addEventListener("click",function(){        alert("node1");
    },true);    node2.addEventListener("click",function(){        alert("node2");
    },true);    node3.addEventListener("click",function(){        alert("node3");
    },true);    node4.addEventListener("click",function(){        alert("node4");
    },true);
</script>
</html>

以上代码分别给node1-node4个节点绑定事件,且采用事件捕获的形式执行,从而分别会弹出node1->node2->node3->node4.

那如果给同一元素同事绑定两个事件,一个采用冒泡一个采用捕获则程序会按照dom流的形式来执行程序即"先捕获后冒泡"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
       <p id="node1">
           <p id="node2">
               <p id="node3">
                   <p id="node4">click me</p>
               </p>
           </p>
       </p>
</body>
<script>    var node1=document.getElementById("node1");    var node2=document.getElementById("node2");    var node3=document.getElementById("node3");    var node4=document.getElementById("node4");    node1.addEventListener("click",function(){        alert("node1");
    },false);    node2.addEventListener("click",function(){        alert("node2");
    },false);    node3.addEventListener("click",function(){        alert("node3");
    },false);    node4.addEventListener("click",function(){        alert("node4");
    },false);    
    
    node1.addEventListener("click",function(){        alert("node1");
    },true);    node2.addEventListener("click",function(){        alert("node2");
    },true);    node3.addEventListener("click",function(){        alert("node3");
    },true);    node4.addEventListener("click",function(){        alert("node4");
    },true);
    
    
</script>
</html>

程序执行顺序:node1->node2->node3->node4->node4->node3->node2->node1.

事件代理

含义:事件代理即事件委托,就是利用事件冒泡只制定一个事件处理程序就可以管理某一类型的所有事件

为什么:一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
<script>    //无事件代理    window.onload = function(){        var oUl = document.getElementById("ul1");        var aLi = oUl.getElementsByTagName(&#39;li&#39;);        for(var i=0;i<aLi.length;i++){            aLi[i].onclick = function(){                alert(123);
            }
        }
    };    //事件代理    window.onload = function(){        var oUl = document.getElementById("ul1");        oUl.onclick = function(ev){            var ev = ev || window.event;            var target = ev.target || ev.srcElement;            if(target.nodeName.toLowerCase() == &#39;li&#39;){                alert(123);                alert(target.innerHTML);
            }
        }
    }
</script>
</body>
</html>

相关推荐:

js中DOM事件绑定详解

JS中DOM如何操作

js中DOM知识点分享

위 내용은 js의 DOM 이벤트 흐름에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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