【基础知识】事件模型

DOM树【Document Object Model】

介绍事件模型之前,先简单看下DOM树的结构:

  • DOM树就像是一颗倒长着的大树,这样的对象模型决定了节点之间都有一定的关联。它们关系可能有父子、有兄弟,我们可以顺着这颗树做出许多操作)。
  • 每个HTML页面都是一颗DOM树,浏览器在解析HTML代码时,会按照代码结构将代码解析成一颗树状结构,每个HTML标签对应DOM树上相应的结点。
  • 下面是一棵最简单的DOM树:
<!DocType html>
    <head>
        <title>eventTest</title>
        <meta name="author" content="Ginna">
        <style>
            div{
              width: 200px;
              height: 100px;
              background-color: red
            }
            ul{
              width:80px;
              height: 80px;
              background-color: blue
            }
            li{
              width:40px;
              height: 40px;
              background-color: yellow
            }
        </style>
    </head>
    <body>
        <div id="c">
            <ul>
                <li>
                    <a>点 我</a>
                </li>
            </ul>
        </div>
        <script>
            var c=document.getElementById('c');
            c.addEventListener('click',function(e){
                console.log(e.target.nodeName)
            });
        </script>
    </body>
</html>

事件模型【捕获与冒泡】


代码中对div点击事件click的监听结果:

  • 当点到a标签区域 “点我” 时,控制台打印出的是A
  • 当点击黄色区域时,打印出 LI
  • 当点击外面蓝色区域时,打印出UL
  • 当点击红色区域时,打印出DIV

上述案例中,我们给div添加一个事件响应函数

var c=document.getElementById('c');
c.addEventListener('click',function(e){
  console.log(e.target.nodeName)
});

事件流

事件流描述的就是从页面中接收事件的顺序。而IE和Netscape提出了完全相反的事件流概念。IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。

打个比喻,如果你把手指放在圆心上(多个同心圆),那么你的手指指向的不是一个圆,而是纸上所有的圆。如果你单击了某个按钮,在单击按钮的同时,也单击了按钮的容器元素,甚至是整个页面。

事件捕获

事件捕获的思想是不太具体的节点应该更早的接收到事件,而在最具体的节点应该最后接收到事件。事件捕获的用以在于事件到达预定目标之前捕获它。IE9+、Safari、Chrome、Opera和Firefox支持,且从window开始捕获(尽管DOM2级事件规范要求从document)。由于老版本浏览器不支持,所以很少有人使用事件捕获。

当点击“点我”的时候,浏览器就会寻找发生点击事件的事件源,从上到下,依次找到body->div->ul->li->a。这个从上到下寻找事件源的过程就叫做事件捕获(阶段)

事件冒泡

IE的事件流叫做事件冒泡。即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。所有现代浏览器都支持事件冒泡,并且会将事件一直冒泡到window对象。

找到了事件源a标签,a标签上的点击事件就会像泡泡一样传递到它的上一级li,进而传递到uldivbody,这个与事件捕获传递方向相反的过程就叫做事件冒泡(阶段)

取消事件冒泡

使用event对象的stopPropagation() 方法。如果在a、li、ul这三个节点任一节点上使用stopPropagation() 方法,冒泡过程就会终止,div就监听不到点击事件,它的响应函数也不会执行

var li=c.getElementsByTagName('li')[0];
li.addEventListener('click',function(e){
  console.log(this.tagName);
  e.stopPropagation();
})

li上取消事件冒泡,这时再点击a标签,事件传递到li上就会停止,console.log只会输出li而不会输出a。如果点击的是ul标签,console.log会输出ul,这是另外一个点击事件,中间没有取消冒泡,妥妥的被div捕捉到。

DOM事件流

  1. DOM0级事件(默认发生在冒泡阶段.只能绑定一个事件)
//事件绑定
 ele.onclick = function (){
     //
 }
//解除绑定
ele.onclick = null;
  1. DOM2级事件(默认发生在冒泡阶段,由第三个参数决定,可绑定多个事件)

DOM2级事件规定事件流包括三个阶段,事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的事件捕获,为截获事件提供了机会。然后是实际的目标接收了事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

//事件绑定
ele.addEventListener(eventType, handler, useCapture)
/**
2015年底,为了扩展新的选项,从而自定义更多的行为
DOM 规范做了修订:addEventListener() 的第三个参数可为 {} 对象
*/
el.addEventListener(type, listener, {
    capture: false, // === useCapture
    once: false,    // 是否设置单次监听
    passive: false  // 是否让 阻止默认行为(preventDefault()) 失效
})
//eventType不带on,如click
//IE下用attachEvent
ele.attachEvent(eventType, handler);
//解除绑定
ele.removeEventListener(eventType,handler);

事件委托

事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果。

事件绑定

  1. 在DOM元素中直接绑定;
<!--
我们可以在DOM元素上绑定
onclick、ondblclick、onkeydown、onkeypress、onkeyup、
onmouseover、onmouseout、onmousedown、onmouseup、等。。
如果想知道更多事件类型请查看, DOM事件 。
-->
<input type="button" value="click me" 
    onclick="hello()">
<script>
    function hello(){
        alert("hello world!");
    }
</script>
  1. 在JavaScript代码中绑定;
<!--JavaScript代码与HTML标签分离,
 文档结构清晰,便于管理和开发。-->
<input type="button" value="click me" id="btn">
<script>
	document.getElementById("btn").onclick = function(){
        alert("hello world!");
	}
</script>
  1. 绑定事件监听函数。
<!--
涉及到上述提到的3个阶段
IE8以下不支持
event : (必需)事件名,支持所有 DOM事件 。
function:(必需)指定要事件触发时执行的函数。
useCapture:(可选)指定事件是否在捕获或冒泡阶段执行。
        true,捕获。false,冒泡。默认false。
        element.addEventListener(event, function, useCapture)
-->
<input type="button" value="click me" id="btn1">
<script>
    document.getElementById("btn1").addEventListener("click",hello);
    function hello(){
      alert("hello world!");
    }
</script>
<!--
ie标准
event:(必需)事件类型。需加on,例如:onclick。
function:(必需)指定要事件触发时执行的函数。
element.attachEvent(event, function)
-->
<input type="button" value="click me" id="btn2">
<script>
    document.getElementById("btn2").attachEvent("onclick",hello);
    function hello(){
         alert("hello world!");
    }
</script>

事件监听

  1. 事件监听的优点
// a.绑定多个事件 (on不行,执行最后一个)
var btn = document.getElementById("btn");
btn.addEventListener("click",fun1);
btn.addEventListener("click",fun2);

// b.可以解除相应的绑定
btn.removeEventListener("click",fun2);
  1. 事件监听的封装
<input type="button" value="click" id="btn">
//绑定监听
function addEventHandler(target,type,fn){
     if(target.addEventListener){
         target.addEventListener(type,fn);
     }else{
         target.attachEvent("on"+type,fn);
     }
}
//移除监听
function removeEventHandler(target,type,fn){
     if(target.removeEventListener){
         target.removeEventListener(type,fn);
     }else{
         target.detachEvent("on"+type,fn);
     }
}
//test,除了click还可以执行其他事件
var btn = document.getElementById("btn");
addEventHandler(btn,"click",fun);
removeEventHandler(btn,"click",fun);

事件委托

事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果

<div id='c'>
    <ul>
        <li>
            <a id='btn'>点 我</a>
        </li>
    </ul>
</div>
<script>
    var c=document.getElementById('c');
    var btn=document.getElementById('btn');
    c.addEventListener('click',function(e){
        if(e.target == btn){
            alert("我是链接呀!!!");
        }
    });
</script>

事件委托优点

  • 提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用。
<!--传统写法-->
<ul id="lst">
 <li id="it1" >item1</li>
 <li id="it2" >item2</li>
</ul>
<script>
    var item1 = document.getElementById("it1");
    var item2 = document.getElementById("it2");
    item1.onclick = function(){
         alert("hello item1");
    }
    item2.onclick = function(){
         alert("hello item2");
    }
</script>

<!--事件委托-->
<ul id="lst">
     <li id="it1" >item1</li>
     <li id="it2" >item2</li>
</ul>
<script>
    var item1 = document.getElementById("it1");
    var item2 = document.getElementById("it2");
    document.addEventListener("click",function(event){
         var target = event.target;
         if(target == item1){
             alert("hello item1");
         }else if(target == item2){
             alert("hello item2");
         }
    })
</script>
  • 动态的添加DOM元素,不需要因为元素的改动而修改事件绑定。
<!--传统写法-->
<ul id="lst">
     <li id="it1" >item1</li>
     <li id="it2" >item2</li>
</ul>
<script>
    var list = document.getElementById("lst");
    var item = list.getElementsByTagName("li");
    for(var i=0;i<item.length;i++){
         (function(i){
             item[i].onclick = function(){
                 alert(item[i].innerHTML);
             }
         })(i)
    }
    var node=document.createElement("li");
    var textnode=document.createTextNode("itemN");
    node.appendChild(textnode);
    list.appendChild(node);
</script>
<!--点击item1/item2都有事件响应,点击itemN时,没有事件响应。
传统的事件绑定无法对[动态添加的元素][动态添加事件]。-->

<!--事件委托-->
<ul id="lst">
     <li id="it1">item1</li>
     <li id="it2">item2</li>
</ul>
<script>
    var list = document.getElementById("lst");
    document.addEventListener("click",function(event){
         var target = event.target;
         if(target.nodeName == "LI"){
             alert(target.innerHTML);
         }
    })
    var node=document.createElement("li");
    var textnode=document.createTextNode("itemN");
    node.appendChild(textnode);
    list.appendChild(node);
</script>
<!--itemN有事件响应。
说明事件委托可以为新添加的DOM元素动态的添加事件。-->

浏览器渲染页面的过程

参考链接

你真的理解事件冒泡和事件捕获吗?

JS 事件绑定、事件监听、事件委托详细介绍

其他…

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值