Home  >  Article  >  Web Front-end  >  A detailed introduction to the graphic code of JavaScript DOM events

A detailed introduction to the graphic code of JavaScript DOM events

黄舟
黄舟Original
2017-03-04 15:40:011143browse

Events are specific interactive moments that occur in a document or browser window, such as click, double-click, mouse hover, etc.

Event flow

Event flow describes the order in which events are received from the page, or the order in which events are propagated in the page.

  • IE's event flow is called event bubbling (event bubbling) : Events are executed starting from the most specific element, and then propagated up to the window step by step object.

  • The event flow proposed by the Netscape team is called event capturing(event capturing): The event starts from the outermost window object, and then gradually propagates downward to The most specific element.

Let’s do a small experiment to illustrate the difference between these two event streams:

// html 文档
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>事件流 实验</title>
</head>
<body>
  <p id="test" style="font-size:3em;">点我点我,我是p</p>
</body>
</html>
// 事件冒泡
var p = document.getElementById("test");
p.addEventListener("click",function(){
  console.log("i am p");
}, false);
document.body.addEventListener("click",function(){
  console.log("i am body");
}, false);
document.documentElement.addEventListener("click",function(){
  console.log("i am html");
}, false);
document.addEventListener("click",function(){
  console.log("i am document");
}, false);
window.addEventListener("click",function(){
  console.log("i am window");
}, false);

A detailed introduction to the graphic code of JavaScript DOM events

点击p,控制端会打印如下:
i am p
i am body
i am html
i am document
i am window
// 事件捕获
var p = document.getElementById("test");
p.addEventListener("click",function(){
  console.log("i am p");
}, true);
document.body.addEventListener("click",function(){
  console.log("i am body");
}, true);
document.documentElement.addEventListener("click",function(){
  console.log("i am html");
}, true);
document.addEventListener("click",function(){
  console.log("i am document");
}, true);
window.addEventListener("click",function(){
  console.log("i am window");
}, true);

A detailed introduction to the graphic code of JavaScript DOM events

The picture has been processed twice and is slightly ugly, please forgive me!

点击p,控制端会打印如下:
i am window
i am document
i am html
i am body
i am p

It can be seen that event bubbling and event capturing are two completely opposite event flows.

Usually, event bubbling is generally used, and event capture is only used under special circumstances.

The event flow specified by DOM2-level events includes three stages: event capture stage, target stage and event bubbling stage.

// 事件冒泡和事件捕获混合一下
var p = document.getElementById("test");
p.addEventListener("click",function(){
  console.log("i am p");
}, true);
document.body.addEventListener("click",function(){
  console.log("i am body");
}, false);    // 改为在冒泡阶段调用事件处理程序
document.documentElement.addEventListener("click",function(){
  console.log("i am html");
}, true);
document.addEventListener("click",function(){
  console.log("i am document");
}, true);
window.addEventListener("click",function(){
  console.log("i am window");
}, false);    // 改为在冒泡阶段调用事件处理程序

This picture is taken directly from the book. Make up your own brain and add the window object yourself!

A detailed introduction to the graphic code of JavaScript DOM events

点击p,控制端会打印如下:
i am document
i am html
i am p
i am body
i am window

Event handler

Event handler: refers to the function that responds to an event

HTML event handlers

Each event supported by an element can be specified using an HTML attribute with the same name as the corresponding event handler.

// 情况一
<button onclick="alert(&#39;I am button!&#39;);">按钮</button>
// 情况二
<button onclick="show();">按钮</button>
<script>
function show(){
  alert("I am button!");
}
</script>

It is recommended that you never use this method to add events to elements, because this method has a huge disadvantage, which is that it makes the HTML and JavaScript codes tightly coupled, which does not comply with the separation of behavior and structure of web design.

DOM0 level event handler

Each element (including window and document) has its own event handler attribute, such as onclick, onmouseup etc.

Rewrite the above code as follows:

<button id="test">按钮</button>
var button = document.getElementById("test");
button.onclick = function(){
  // this 对象指向 button 元素
  console.log(this);
  alert("I am button!");
};
/*  删除指定的事件处理程序  */
button.onclick = null;

DOM2 level event handler

DOM2 level event defines two Methods used to handle the operations of specifying and removing event handlers: addEventListener() and removeEventListener().

Both methods have three parameters. The first parameter is the event name, the second parameter is the function as the event handler, and the third parameter is a Boolean value. The default value is false, which means The event handler is called during the bubbling phase. If this value is true, it means the event handler is called during the capture phase.

Continue to rewrite the above code:

var button = document.getElementById("test");
function show(){
  // this 对象指向 button 元素
  console.log(this);
  alert("I am button!");
}
// 添加事件处理程序
button.addEventListener("click",show, false);
button.addEventListener("click",function(){
  alert("I am second alert!");
},false);
// 删除事件处理程序
button.removeEventListener("click",show, false); // 删除成功
button.removeEventListener("click",function(){   // 删除失败
  alert("I am second alert!");
},false);

The main benefit of using the DOM2-level method to add event handlers is that you can add multiple event handlers for an element.

If the event handler added by addEventListener() is an anonymous function, the event handler cannot be deleted by removeEventListener().

IE event handler

IE8 and earlier IE versions only support event bubbling and do not support addEventListener() and removeEventListener(), but It implements two methods similar to these two methods: attachEvent() and detachEvent(). These two methods have only two parameters, an event name (onclick, not click), and an event handling function.

Different from addEventListener(), when using the attachEvent() method, the event handling function will run in the global scope, so this is equal to window, which requires special attention.

var button = document.getElementById("test");
button.attachEvent("onclick", function(){
  // this 对象指向了全局作用域,即 window
  console.log(this);
  alert("I am button!");
});
button.attachEvent("onclick",function(){
  alert("I am second alert!");
}); 
// 上面也是为同一个按钮绑定了两个事件处理程序
button.detachEvent("onclick",function(){ // 删除失败
  alert("I am second alert!");
});

Do not use these two functions unless you want to make your program compatible with IE8-browser.

Browser default behavior

For some specific events, the browser has its default behavior. For example: clicking a link will jump to the specified page, right-clicking the mouse will bring up the browser's right-click menu, and pressing Enter when filling out a form will automatically submit it to the server, etc.

The default browser behavior and bubbling behavior are independent. Canceling the default event behavior will not cancel the event bubbling, and vice versa. Multiple event handlers for the same element are also independent of each other.

<p>
  <a id="test" href="http://baidu.com">点我点我,我是链接</a>
</p>
var link = document.getElementById("test");
// 方法一   event.preventDefault()
link.onclick = function(event){
  console.log("You click!");
  event.preventDefault();
};
// 方法二   return false
link.onclick = function(event){
  console.log("You click!");
  return false;
};

Reference:Event-4. Browser default behavior-Yu Qing

Event object event

When an event on the DOM is triggered, an event object event is generated, which contains all information related to the event.

event 对象只存在于事件处理程序执行期间,一旦执行完毕,立即被销毁。

<p>
  <a id="test" href="http://baidu.com">点我点我,我是链接</a>
</p>
/* 下列 event 对象的属性和方法都是只读的  */
var link = document.getElementById("test");
link.onclick = function(event){
  // 判断当前事件是否会向 DOM 树上层元素冒泡
  console.log(event.bubbles);

  // 判断是否可以取消事件的默认行为
  console.log(event.cancelable);
  // 使用该方法可以取消事件的默认行为(使用前提是 cancelable 属性的值为 true)
  event.preventDefault();
  // 判断是否已经调用了 preventDefault() 方法(DOM3级事件新增)
  console.log(event.defaultPrevented);

  // 指向事件遍历 DOM 时,识别事件的当前目标对象
  console.log(event.currentTarget);
  // 指向触发事件的对象
  console.log(event.target);

  // 表示事件流当前处于哪一个阶段
  // 值为 1 表示在捕获阶段,值为 2 表示处于目标阶段,值为 3 表示在冒泡阶段
  console.log(event.eventPhase);

  // 返回一个字符串, 表示该事件对象的事件类型
  console.log(event.type);

  // 立即停止当前事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡
  event.stopPropagation();
};

为了进一步说明 event.stopPropagation() 的运行效果,借用前面的代码,更改如下:

// 事件冒泡和事件捕获混合一下
var p = document.getElementById("test");
p.addEventListener("click",function(){
  console.log("i am p");
}, true);
document.body.addEventListener("click",function(){
  console.log("i am body");
}, false);    // 改为在冒泡阶段调用事件处理程序
document.documentElement.addEventListener("click",function(){
  console.log("i am html");
  event.stopPropagation();    // 立即停止事件在 DOM 中的传播
}, true);
document.addEventListener("click",function(){
  console.log("i am document");
}, true);
window.addEventListener("click",function(){
  console.log("i am window");
}, false);    // 改为在冒泡阶段调用事件处理程序
点击p,控制端会打印如下:
i am document
i am html

看到了吧,后面元素的事件就不会被激发了。

事件类型

UI 事件

UI 事件指的是简单的用户界面事件

load 事件:当页面所有资源(比如图像、css文件、js文件等资源)完全加载完毕后,就会触发 window 上面的 load 事件。

当然,我们也可以单独为某个元素设置 load 事件,比如为一个图片绑定这个事件,就可以检测这个图片是否加载完毕了。

<img  id="myImg" src="1.jpg" / alt="A detailed introduction to the graphic code of JavaScript DOM events" >
var img = document.getElementById("myImg");
img.onload = function(){
  alert("Image loaded!");
};

scroll 事件:在文档被滚动期间,重复触发该事件。

window.onscroll = function(){
  console.log("Scroll!");
};

焦点事件

焦点事件会在页面元素获得或失去焦点时触发。

注意:默认情况下,只有部分 html 元素能获得鼠标焦点(如 input,a),很大一部分 html 元素是不能获得鼠标焦点的(如 p,img),这些能够获得鼠标焦点的元素就是 focusable 元素。

不过,可以通过为这些默认没有焦点事件的元素添加一个 tabindex 属性,从而使它可以支持焦点事件。

  • document.activeElement:返回当前页面中获得焦点的元素,如果没有某个元素获得焦点,则该属性的值为当前页面中的元素

  • document.hasFocus():判断当前文档或者当前文档的子节点是否获得了焦点

  • HTMLElement.focus():使得指定的元素获得焦点(前提是这个元素是一个可以获得焦点的元素)

  • HTMLElement.blur():移除当前元素获得的焦点

上面是一些 HTML5 中添加的焦点管理 API。下面说焦点事件:

  • focus 事件:在元素获得焦点时触发,这个事件不会冒泡

  • blur 事件:在元素失去焦点时触发,这个事件不会冒泡

  • focusin 事件:在元素获得焦点时触发,与 focus 事件等价,但会冒泡

  • focusout 事件:在元素失去焦点时触发,与 blur 事件等价,但会冒泡

<img  id="img" src="1.jpg" tabindex="1" / alt="A detailed introduction to the graphic code of JavaScript DOM events" >
var img = document.getElementById("img");
document.body.onscroll = function() {
    img.blur();
};
img.onfocus = function() {
    console.log("Img focused!");
};
img.onblur = function() {
    console.log("Img lose focus!");
};
img.onclick = function() {
    console.log(document.activeElement);
    console.log(document.hasFocus());
};

注意: focusin 和 focusout 事件,所有的 Firefox 版本都不支持,看这里。chrome 和 safari 中只有通过 addEventListener 方式绑定这两个事件才能正常使用,其他方式绑定都不行。

说说focus /focusin /focusout /blur 事件(推荐)
(WHATWG)Focus management APIs

鼠标与滚轮事件

DOM3 级事件中定义了9个鼠标事件。

<body>
 <p id="test">
   <button>按钮</button>
 </p>
</body>
// css
p {
    width: 300px;
    height: 300px;
    margin: 50px auto;
    border: 1px solid #aaa;
  }

mouseover 事件:鼠标指针首次进入一个元素边界之内时触发,会冒泡

mouseout 事件:鼠标指针移出这个元素边界时触发,会冒泡

var test = document.getElementById("test");
test.onmouseover = function(event){
  event.target.style.border = "3px solid #666";
};
test.onmouseout = function(event){
  event.target.style.border = "none";
};

mouseenter 事件:鼠标指针首次移动到元素范围之内时触发,不冒泡

mouseleave 事件:鼠标指针移动到元素范围之外时触发,不冒泡

var test = document.getElementById("test");
test.onmouseenter = function(event){
  event.target.style.border = "10px solid #666";
};
test.onmouseleave = function(event){
  event.target.style.border = "none";
};

mousemove 事件:当鼠标在元素内部移动时重复触发,会冒泡

// 这里写一个鼠标在 body 内移动时,背景颜色随机变化的脚本
document.body.onmousemove = function(){
  var r = Math.floor(Math.random() * 256)
      g = Math.floor(Math.random() * 256)
      b = Math.floor(Math.random() * 256);
  document.body.style.background = "rgb("+r+","+g+","+b+")";
};

mousedown 事件:用户通过按下任意鼠标按钮时触发

mouseup 事件:用户释放鼠标按钮时触发

click 事件:用户单击鼠标左键时或按下回车键时触发

dblclick 事件:用户双击鼠标左键时触发

var body = document.body;
body.onmousedown = function(){
  console.log("Mouse down!");
};
body.onmouseup = function(){
  console.log("Mouse up!");
};
body.onclick = function(){
  console.log("One click!");
};
body.ondblclick = function(){
  console.log("Double click!");
};
// 双击 body,控制端打印:
Mouse down!
Mouse up!
One click!
Mouse down!
Mouse up!
One click!
Double click!

只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件,缺一不可。类似地,只有连续触发两次 click 事件,才会触发 dblclick 事件。

获取点击坐标

  • clientX 和 clientY:鼠标指针在视口中的坐标

  • pageX 和 pageY:鼠标指针在页面中的坐标

  • screenX 和 screenY:鼠标指针在屏幕中的坐标

在页面没有滚动的情况下,pageX 和 pageY 的值与 clientX 和 clientY 的值相等。

document.body.onclick = function() {
  console.log("Client: " + "(" + event.clientX + "," + event.clientY + ")");
  console.log("Page: " + "(" + event.pageX + "," + event.pageY + ")");
  console.log("Screen: " + "(" + event.screenX + "," + event.screenY + ")");
};

键盘与文本事件

  • keydown 事件:当用户按下任意键时触发,而且如果按住不放的话,会重复触发此事件

  • keypress 事件:当用户按下任意字符键时触发,而且如果按住不放的话,会重复触发此事件

  • keyup 事件:当用户释放键盘上的按键时触发

在用户按下一个字符键并且立马释放这个按键的过程中,先触发 keydown 事件,再触发 keypress 事件,最后触发 keyup 事件。

window.onkeydown = function() {
  console.log("on key down");
};
window.onkeyup = function() {
  if (event.keyCode === 65) {
    console.log(event.key);
  }
  console.log("one key up");
};
window.onkeypress = function() {
  console.log("one key press");
};

为了知道自己按下的是哪个按键,可以使用 event.which 来获得你按下按键的键码(比如字母 A 的键码为 65)。也可以通过键码属性(event.keyCode)来对特定的按键来进行响应。键码参考表

发生 keypress 事件时,会存在一个 charCode 属性,返回这个按键代表字符的 ASCII 编码。ASCII table

event.which 返回一个 keyCode 或 charCode 值,详情看下面代码

String.fromCharCode() 静态方法可以将 event.which 转化为相应的字符。

// 请自行粘贴复制于浏览器控制端测试
// 分别输入 a 和 A
// A和a 的 ASCII码分别为65,97
window.onkeydown = function(){
  if(event.keyCode === 65){
    console.log("(keydown keyCode)You press &#39;a&#39; or &#39;A&#39;!");
  }
  if(event.charCode === 65){
    console.log("(keydown charCode)You press &#39;A&#39;!");
  }
  console.log("keydown event.which = " + event.which);
  console.log("(keydown:)" + String.fromCharCode(event.which));
};
window.onkeypress = function(){
  if(event.keyCode === 65){
    console.log("(keypress keyCode)You press &#39;A&#39;!");
  }
  if(event.charCode === 65){
    console.log("(keypress charCode)You press &#39;A&#39;,not &#39;a&#39;!");
  }
  console.log("keypress event.which = " + event.which);
  console.log("(keypress:)" + String.fromCharCode(event.which));
};

在 DOM3 级事件中,做了一些变化,不再推荐使用 keyCode、charCode、which 属性,而是出现了两个新属性:key 和 char,用来替代 keyCode 和 charCode 属性。

但是不建议使用 key、char 属性,因为有一部分浏览器对他支持的不是很好。点这里

参考:JavaScript 事件——“事件类型”中“键盘与文本事件”的注意要点

变动事件

变动事件是在 DOM 结构发生变化时触发

在 DOM2 中定义了多个变动事件,但是在 DOM3 中又废除了一些,在这里先简单提一下有这个东西,以后用到再作补充。

HTML5 事件

contextmenu 事件:单击鼠标右键,会触发这个事件并调出页面的上下文菜单。这个事件是鼠标事件的一种,并且支持冒泡。

在上面的例子中,我们演示了利用 contextmenu 事件来设置自定义右键菜单,以及屏蔽右键菜单。

然而,屏蔽右键菜单却难不倒高手,Greasy Fork 上面有一个脚本(网页限制解除),专门来打破对右键菜单有限制的页面。

beforeunload 事件:当浏览器窗口,文档或其资源将要卸载时或者刷新页面时,会触发这个事件【MDN】

下面有几点需要注意一下:

1. 如果处理函数为 Event 对象的 returnValue 属性赋值非空字符串,浏览器会弹出一个对话框,来询问用户是否确定要离开当前页面。没有赋值时,该事件不做任何响应。

2. 从2011年5月25号开始,HTML5规范指出在此事件处理函数中,对于window.alert(), window.confirm(), 和 window.prompt() 的调用会被忽略。

3. 旧版本的浏览器可能会在提示框里显示返回的信息,但是新版本浏览器都默认采用浏览器内设的提示信息。

// 复制粘贴于浏览器控制端,然后关闭页面,会出现提示框
window.addEventListener("beforeunload", function() {
  var message = "你真的要离开吗?";
  event.returnValue = message;
  return message;
}, false);

DOMContentLoaded 事件:在页面文档形成完整的 DOM 树时触发,不理会图像、js文件、css文件等资源是否加载完毕

这个事件可以为 document 或 window 绑定,但实际目标是 document。这个事件会冒泡。

注意:这个事件与 load 事件是不一样的,正常情况下,会早于 load 事件被触发。

感觉篇幅太长了,事件委托移动设备中的事件事件对内存性能的影响等内容将在以后逐渐写出。

以上就是JavaScript DOM 事件初探的图文代码详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn