JavaScript事件机制

事件是javascript和HTML交互基础, 任何文档或者浏览器窗口发生的交互, 都要通过绑定事件进行交互。
在了解事件之前先看看DOM(Document Object Model)

1 DOM 节点

DOM即文档对象模型,文档抽象为一个树形结构,文档中的标签、标签属性或标签内容表示为树上的节点。

DOM按操作对象分类:

  • Core Dom:核心Dom,针对任何结构化文档的标准模型。

  • XML DOM:用于XML文档的标准模型,对XML元素进行操作。

  • HTML DOM: 用于HTML文档的标准模型,对HTML元素进行操作。

文档中的所有内容都可表示为一个节点(node),节点分类:

  1. 文档节点(Document):整个XML、HTML文档

  2. 元素节点(Element):每个XML、HTML元素

  3. 属性节点(Attr):每个XML、HTML元素的属性

  4. 文本节点(Text):每个XML、HTML元素内的文本

  5. 注释节点(Comment):每个注释

DOM节点属性:

  1. innerHtml:以HTML代码格式获取或设置节点的内容
  2. innerText:获取或设置节点的文本内容

  3. nodeName:获取节点名称,只读属性

节点类型nodeName
文档节点(Document)返回 #document
元素节点(Element)返回 大写元素名称
属性节点(Attr)返回 属性名称
文本节点(Text)返回 #text

4. nodeValue:获取或设置节点的值

节点类型nodeValue
文档节点(Document)只读,返回null
元素节点(Element)只读,返回null
属性节点(Attr)获取或设置属性的值
文本节点(Text)获取或设置文本的值

5. nodeType:返回节点类型,只读属性

节点类型nodeType
文档节点(Document)9
元素节点(Element)1
属性节点(Attr)2
文本节点(Text)3

获取HTML 元素节点方法:

  • getElementById(id) :获取指定ID的元素
  • getElementsByName(name) :返回一个包含指定name名称的的元素数组
  • getElementsByClassName(className) :返回一个包含指定class名称的的元素数组
  • getElementsByTagName(elementName) :返回一个指定标签名称的的元素数组
2 事件模型

DOM0级:
DOM0级事件模型事件注册与解除栗子

<p id = 'click'>click me</p>
<script>
    document.getElementById('click').onclick = function(event){
        alert(event.target);
        console.log(you click the first function);
    }
    document.getElementById('click').onclick = function(event){
        alert(event.target);
        console.log(you click the second function);
    }
</script>
document.getElementById('click'_).onclick = null;

一个dom对象只能注册一个同类型的函数,因为注册多个同类型的函数的话,就会发生覆盖。这种方式所有浏览器都兼容,但是逻辑与显示并没有分离。

DOM2级

在制定统一标准前,网景主张捕获方式,微软主张冒泡方式。后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡。先借两张图看看事件在事件流中的传播
event model

evnetflow

DOM2级事件包括三个阶段:
1. 事件捕获阶段:document 往事件触发地点,捕获前进,遇到相同注册事件立即触发执行
2. 处于目标阶段:到达事件位置,触发事件(按照事件注册顺序执行)
3. 事件冒泡阶段事件触发地点往 document 方向,冒泡前进,遇到相同注册事件立即触发

在DOM2级中使用addEventListener和removeEventListener来注册和解除事件,一个dom对象可以注册多个相同类型的事件,不会发生事件的覆盖,在各阶段会依次的执行各个事件函数。

事件绑定监听函数的方式如下:
addEventListener(eventType, handler, useCapture)
事件移除监听函数的方式如下:
removeEventListener(eventType, handler, useCapture)

参数说明:
eventType指定事件类型(不要加on)
handler是事件处理函数
useCapture是一个boolean用于指定是否在捕获阶段进行处理(考虑浏览器兼容,一般设置为false与IE浏览器保持一致)

一个栗子

<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
        <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
    </div>
    <script>
        var click = document.getElementById('inner');
        click.addEventListener('click',function(event){
        alert('inner show capture show');
        event.stopPropagation();
        },true);
        click.addEventListener('click',function(){
            alert('click one bubble show');
        },false);
        click.addEventListener('click',function(){
            alert('click two bubble show');
        },false);

        var clickouter = document.getElementById('outer');

        clickouter.addEventListener('click',function(event){
            alert('outer show capture show');
            // event.stopPropagation();
            // event.stopImmediatePropagation();
        },true);
        clickouter.addEventListener('click',function(){
            alert('outer show bubble show');
        },false);
    </script>

在目标元素上就不会遵守先发生捕获后发生冒泡这一规则,而是先绑定的事件先发生。useCapture只对捕获和冒泡阶段有用,对目标阶段无效。DOM1事件发生在冒泡阶段。
stopPropagation()只阻止事件在其他节点上继续传播
stopImmediatePropagation() 立即阻止事件传播
关于事件流可以看一个DEMO

3 事件委托

在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先,每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click事件会一直冒泡到document层次。也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。
从兼容性角度来说还是建议大家使用事件冒泡模型。事件委托还有一个好处就是添加进来的元素也能绑定事件

来个栗子

<ul>
     <li>item1</li>
     <li>item2</li>
     <li>item3</li>
     <li>item4</li>
     <li>item5</li>
     <li>item6</li>
 </ul>
<script type="text/javascript">
$("ul").on("mouseover",function(e){
    $(e.target).css("background-color","#ddd").siblings().css("background-color","white");
            })
</script> 
4 一个扩展

由于事件模型的差异以及Event对象的不同,为了达到兼容各个浏览器的目的,我们可以增加一个Event Wrapper, 它对各个浏览器应当提供一致的事件操作接口。

<body>
  <button id="btn">button</button>
  <button id="button">button</button>
<script type="text/javascript">
(function(){
    var eventUtils={
     // 添加句柄
     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.removeEventListener(type,handler,false);
       }else if(element.detachEvent){
         element.detachEvent('on'+type,handler);
       }else{
         element['on'+type]=null;
       }
     },
    //获取事件对象
    //IE模型中event是一个全局唯一的对象绑定在window对象上
    getEvent:function(event){
       return event?event:window.event;
    },
    //获取类型
    getType:function(event){
     return event.type;
    },
    getElement:function(event){
     return 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;
    }
   }
  };

  var btn = document.getElementById('btn');
  var handler = function() {
    var event = arguments[0];
    console.log('debugger');
    console.log(eventUtils.getEvent(event));
    console.log(eventUtils.getType(event));
    console.log(eventUtils.getElement(event));
    event.stopImmediatePropagation(); // 立即阻止事件传播,stopPropagation()只阻止事件在其他节点上继续传播
  };
  eventUtils.addHandler(btn, 'click', handler);

  var fun = function() {
    console.log('fun');
  };

  var button = document.getElementById('btn');
  var event = new Event('threeclick', {'bubbles':true, 'cancelable':false}); // 自定义事件
  eventUtils.addHandler(button, 'threeclick', handler);
  eventUtils.addHandler(button, 'threeclick', fun);
  button.dispatchEvent(event);

})();
</script>

更多内容
[1] HTML DOM 介绍
[2] 理解javascript中的事件模型
[3] 彻底弄懂JS的事件冒泡和事件捕获
[4] 事件冒泡、事件捕获和事件委托
[5] JavaScript 详说事件机制之冒泡、捕获、传播、委托
[6] DOM0,DOM2,DOM3事件,事件基础知识入门
[7] 事件冒泡和事件捕获
[8] [HTML] target phase事件顺序
[9] Event dispatch and DOM event flow
[10] EVENT FLOW DEMO
[11] JS事件模型-观察者模式
[12] [解惑]JavaScript事件机制
[13] 【前端盲点】事件的几个阶段你真的了解么???
[14] 【探讨】javascript事件机制底层实现原理

如果内容信息侵犯了您的合法权益,请告知我,我将及时处理。
博客内容仅作学习/交流/参考之用,欢迎大家交流探讨;E-Mail:dwang2014#hotmail.com(#@)
站在巨人的肩上才能看得更远,一步一个脚印才能走得更远。分享成长,交流进步,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值