关于javascript:DOM事件机制

2次阅读

共计 5788 个字符,预计需要花费 15 分钟才能阅读完成。

前言

本文次要介绍 DOM 事件级别、DOM 事件模型、事件流、事件代理和 Event 对象常见的利用,心愿对你们有些帮忙和启发!

一、DOM 事件级别

DOM 级别一共能够分为四个级别:DOM0 级、DOM1 级、DOM2 级和 DOM3 级。而DOM 事件分为 3 个级别:DOM 0 级事件处理,DOM 2 级事件处理和 DOM 3 级事件处理。因为 DOM 1 级中没有事件的相干内容,所以没有 DOM 1 级事件。

1.DOM 0 级事件

el.onclick=function(){}

// 例 1
var btn = document.getElementById('btn');
 btn.onclick = function(){alert(this.innerHTML);
 }

当心愿为同一个元素 / 标签绑定多个同类型事件的时候(如给下面的这个 btn 元素绑定 3 个点击事件),是不被容许的 。DOM0 事件绑定,给元素的事件行为绑定办法, 这些办法都是在以后元素事件行为的冒泡阶段 (或者指标阶段) 执行的

2.DOM 2 级事件

el.addEventListener(event-name, callback, useCapture)

  • event-name: 事件名称,能够是规范的 DOM 事件
  • callback: 回调函数,当事件触发时,函数会被注入一个参数为以后的事件对象 event
  • useCapture: 默认是 false,代表事件句柄在冒泡阶段执行 **
// 例 2
var btn = document.getElementById('btn');
btn.addEventListener("click", test, false);
function test(e){
    e = e || window.event;
    alert((e.target || e.srcElement).innerHTML);
    btn.removeEventListener("click", test)
}
//IE9-:attachEvent()与 detachEvent()。//IE9+/chrom/FF:addEventListener()和 removeEventListener()

IE9 以下的 IE 浏览器不反对 addEventListener()和 removeEventListener(),应用 attachEvent()与 detachEvent() 代替,因为 IE9 以下是不反对事件捕捉的,所以也没有第三个参数,第一个事件名称前要加 on。

3.DOM 3 级事件

在 DOM 2 级事件的根底上增加了更多的事件类型。

  • UI 事件,当用户与页面上的元素交互时触发,如:load、scroll
  • 焦点事件,当元素取得或失去焦点时触发,如:blur、focus
  • 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dblclick、mouseup
  • 滚轮事件,当应用鼠标滚轮或相似设施时触发,如:mousewheel
  • 文本事件,当在文档中输出文本时触发,如:textInput
  • 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
  • 合成事件,当为 IME(输入法编辑器)输出字符时触发,如:compositionstart
  • 变动事件,当底层 DOM 构造发生变化时触发,如:DOMsubtreeModified
  • 同时 DOM3 级事件也容许使用者自定义一些事件。

二、DOM 事件模型和事件流

DOM 事件模型分为捕捉和冒泡。一个事件产生后,会在子元素和父元素之间流传(propagation)。这种流传分成三个阶段。

(1)捕捉阶段:事件从 window 对象自上而下向指标节点流传的阶段;

(2)指标阶段:真正的指标节点正在处理事件的阶段;

(3)冒泡阶段:事件从指标节点自下而上向 window 对象流传的阶段。

DOM 事件捕捉的具体流程

捕捉是从上到下,事件先从 window 对象,而后再到 document(对象),而后是 html 标签(通过document.documentElemen t 获取 html 标签),而后是 body 标签(通过document.body 获取 body 标签),而后依照一般的 html 构造一层一层往下传,最初达到指标元素。

而事件冒泡的流程刚好是事件捕捉的逆过程。
接下来咱们看个事件冒泡的例子:

// 例 3
<div id="outer">
    <div id="inner"></div>
</div>
<script>
......
window.onclick = function() {console.log('window');
};
document.onclick = function() {console.log('document');
};
document.documentElement.onclick = function() {console.log('html');
};
document.body.onclick = function() {console.log('body');
}
outer.onclick = function(ev) {console.log('outer');
};
inner.onclick = function(ev) {console.log('inner');
};
</script>

正如咱们下面提到的 onclick 给元素的事件行为绑定办法都是在以后元素事件行为的冒泡阶段 (或者指标阶段) 执行的。

三、事件代理(事件委托)

因为事件会在冒泡阶段向上流传到父节点,因而能够把子节点的监听函数定义在父节点上,由父节点的监听函数对立解决多个子元素的事件。这种办法叫做事件的代理(delegation)。

1. 长处

  • 缩小内存耗费,进步性能

假如有一个列表,列表之中有大量的列表项,咱们须要在点击每个列表项的时候响应一个事件

// 例 4
<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

如果给每个列表项一一都绑定一个函数,那对于内存耗费是十分大的,效率上须要耗费很多性能。借助事件代理,咱们只须要给父容器 ul 绑定办法即可,这样不论点击的是哪一个后辈元素,都会依据冒泡流传的传递机制,把容器的 click 行为触发,而后把对应的办法执行,依据事件源,咱们能够晓得点击的是谁,从而实现不同的事。

  • 动静绑定事件

在很多时候,咱们须要通过用户操作动静的增删列表项元素,如果一开始给每个子元素绑定事件,那么在列表发生变化时,就须要从新给新增的元素绑定事件,给行将删去的元素解绑事件,如果用事件代理就会省去很多这样麻烦。

2. 如何实现

接下来咱们来实现上例中父层元素 #list 下的 li 元素的事件委托到它的父层元素上:

// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性解决
  var event = e || window.event;
  var target = event.target || event.srcElement;
  // 判断是否匹配指标元素
  if (target.nodeName.toLocaleLowerCase === 'li') {console.log('the content is:', target.innerHTML);
  }
});

四、Event 对象常见的利用

  • event. preventDefault()

如果调用这个办法,默认事件行为将不再触发 。什么是默认事件呢?例如表繁多点击提交按钮(submit) 跳转页面、a 标签默认页面跳转或是锚点定位等。

很多时候咱们应用 a 标签仅仅是想当做一个一般的按钮,点击实现一个性能,不想页面跳转,也不想锚点定位。

// 办法一:<a href="javascript:;"> 链接 </a>

也能够通过 JS 办法来阻止,给其 click 事件绑定办法,当咱们点击 A 标签的时候,先触发 click 事件,其次才会执行本人的默认行为

// 办法二:
<a id="test" href="http://www.cnblogs.com"> 链接 </a>
<script>
test.onclick = function(e){
    e = e || window.event;
    return false;
}
</script>
// 办法三:<a id="test" href="http://www.cnblogs.com"> 链接 </a>
<script>
test.onclick = function(e){
    e = e || window.event;
    e.preventDefault();}
</script>

接下来咱们看个例子:输入框最多只能输出六个字符,如何实现?

// 例 5
 <input type="text" id='tempInp'>
 <script>
    tempInp.onkeydown = function(ev) {
        ev = ev || window.event;
        let val = this.value.trim() //trim 去除字符串首位空格(不兼容)// this.value=this.value.replace(/^ +| +$/g,'') 兼容写法
        let len = val.length
        if (len >= 6) {this.value = val.substr(0, 6);
            // 阻止默认行为去除非凡按键(DELETE\BACK-SPACE\ 方向键...)let code = ev.which || ev.keyCode;
            if (!/^(46|8|37|38|39|40)$/.test(code)) {ev.preventDefault()
            }
        }
    }
 </script>
  • event.stopPropagation() & event.stopImmediatePropagation()

event.stopPropagation() 办法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行 。下面提到事件冒泡阶段是指事件从指标节点自下而上向 window 对象流传的阶段。
咱们在例 3 的 inner 元素 click 事件上,增加 event.stopPropagation() 这句话后,就阻止了父事件的执行,最初只打印了 ’inner’。

 inner.onclick = function(ev) {console.log('inner');
    ev.stopPropagation();};

stopImmediatePropagation 既能阻止事件向父元素冒泡,也能阻止元素同事件类型的其它监听器被触发。而 stopPropagation 只能实现前者的成果。咱们来看个例子:

<body>
  <button id="btn">click me to stop propagation</button>
</body>
<script>
......
const btn = document.querySelector('#btn');
btn.addEventListener('click', event => {console.log('btn click 1');
  event.stopImmediatePropagation();});
btn.addEventListener('click', event => {console.log('btn click 2');
});
document.body.addEventListener('click', () => {console.log('body click');
});
// btn click 1
</script>

如上所示,应用 stopImmediatePropagation 后,点击按钮时,不仅 body 绑定事件不会触发,与此同时按钮的另一个点击事件也不触发。

  • event.target & event.currentTarget

诚实说这两者的区别,并不好用文字描述,咱们先来看个例子:

<div id="a">
  <div id="b">
    <div id="c"><div id="d"></div></div>
  </div>
</div>
<script>
  document.getElementById('a').addEventListener('click', function(e) {
    console.log('target:' + e.target.id + '&currentTarget:' + e.currentTarget.id)
  })
  document.getElementById('b').addEventListener('click', function(e) {
    console.log('target:' + e.target.id + '&currentTarget:' + e.currentTarget.id)
  })
  document.getElementById('c').addEventListener('click', function(e) {
    console.log('target:' + e.target.id + '&currentTarget:' + e.currentTarget.id)
  })
  document.getElementById('d').addEventListener('click', function(e) {
    console.log('target:' + e.target.id + '&currentTarget:' + e.currentTarget.id)
  })
</script>

当咱们点击最里层的元素 d 的时候,会顺次输入:

target:d&currentTarget:d
target:d&currentTarget:c
target:d&currentTarget:b
target:d&currentTarget:a

从输入中咱们能够看到,event.target指向引起触发事件的元素,而 event.currentTarget 则是事件绑定的元素,只有被点击的那个指标元素的 event.target 才会等于 event.currentTarget 也就是说,event.currentTarget始终是监听事件者,而 event.target 是事件的真正收回者

后记

如需前端领导、前端材料、Java 领导和 Java 材料的请分割自己,感谢您的反对。

WECHAT:xzsj07
备注:加好友请注明起源。前 10 名读者赠送 JS 或 Java 书籍一本,RK61 机械键盘一个。

正文完
 0