Interpretation of JavaScript events
1. Basic concept of events
Event refers to Some specific interaction moments that occur in a document or browser, such as opening a certain web page, the load
event will be triggered after the browser is loaded, and hover will be triggered when the mouse hovers over an element.
event, when the mouse clicks on an element, the click
event will be triggered, etc.
Event handling is the behavior of the browser in response to the event when the event is triggered, and the code corresponding to this behavior is Event handler.
2. Event operations: monitoring and removing monitoring
2.1 Monitoring events
The browser will perform corresponding event processing based on some events. The premise of event processing is that it needs There are three main ways to listen to events:
2.1.1 HTML inline attributes
means directly filling in the event-related attributes in the HTML element, and the attribute value is event processing program. The example is as follows:
<button></button>
onclick
corresponds to the click
event, so when the button is clicked, the event handler will be executed, that is, the console output You clicked me!
.
However, we need to point out that this method couples HTML code and JavaScript code, which is not conducive to code maintenance, so this method should be avoided as much as possible.
2.1.2 DOM attribute binding
Specify events and event handlers by directly setting the properties of a DOM node. The above code:
const btn = document.getElementById("btn"); btn.onclick = function(e) { console.log("You clicked me!"); };
In the above example, First obtain the btn object and listen to the click
event by adding the onclick
attribute to this object. This attribute value corresponds to the event handler. This program is also called the DOM level 0 event handler.
2.1.3 Event listening function
The standard event listening function is as follows:
const btn = document.getElementById("btn"); btn.addEventListener("click", () => { console.log("You clicked me!"); }, false);
The above example means first obtaining the btn object representing the node, and then adding it to this object An event listener is created. When the click
event occurs, the callback function is called, that is, You clicked me!
is output on the console. addEventListener
The function contains three parameters false. The meaning of the third parameter will be explained after the three stages of event triggering. This program is also called a DOM level 2 event handler. IE9, FireFox, Safari, Chrome and Opera all support DOM level 2 event handlers. For IE8 and below, use the attacEvent()
function to bind events.
So we can write a piece of code with compatibility:
function addEventHandler(obj, eventName, handler) { if (document.addEventListener) { obj.addEventListener(eventName, handler, false); } else if (document.attachEvent) { obj.attachEvent("on" + eventName, handler); } else { obj["on" + eventName] = handler; } }
2.2 Remove event listening
After binding an event to an element, if you want to contact Binding, you need to use the removeEventListener
method. Look at the following example:
const handler = function() { // handler logic } const btn = document.getElementById("btn"); btn.addEventListener("click", handler); btn.removeEventListener("click", handler);
It should be noted that the callback function of the bound event cannot be an anonymous function, but must be a declared function, because the reference of this callback function needs to be passed when unbinding the event.
Similarly, IE8 and below versions do not support the above method, but use detachEvent
instead.
const handler = function() { // handler logic } const btn = document.getElementById("btn"); btn.attachEvent("onclick", handler); btn.detachEvent("onclick", handler);
Similarly, you can write a compatible deletion event function:
function removeEventHandler(obj, eventName, handler) { if (document.removeEventListener) { obj.removeEventListener(eventName, handler, false); } else if (document.detachEvent) { obj,detachEvent("on" + eventName, handler); } else { obj["on" + eventName] = null; } }
3. Event triggering process
The event stream describes the order in which the page receives events. The event flow of modern browsers (referring to browsers except IE6-IE8, including IE9, FireFox, Safari, Chrome and Opera, etc.) includes three processes, namely the capture phase, the target phase and the bubbling phase. The following figure vividly illustrates this Process:
The following will explain these three processes in detail.
3.1 Capture phase
When we operate on a DOM element, such as mouse click, hover, etc., an event will be transmitted to the DOM element. This event starts from Window and goes through in sequence. docuemnt, html, body, and then continue to pass through the child nodes until it reaches the target element. The process of reaching the parent node of the target element from the Window is called Capture phase. Note that the target node has not yet been reached at this time.
3.2 Target phase
At the end of the capture phase, the event reaches the parent node of the target node, and finally reaches the target node, and triggers the event on the target node. This is the goal stage.
It should be noted that the target node triggered by the event is the lowest node. For example, the following example:
<div> <p>你猜,目标在这里还是<span>那里</span>。</p> </div>
当我们点击“那里”的时候,目标节点是<span></span>
,点击“这里”的时候,目标节点是<p></p>
,而当我们点击<p></p>
区域之外,<p></p>
区域之内时,目标节点就是<p></p>
。
3.3 冒泡阶段
当事件到达目标节点之后,就会沿着原路返回,这个过程有点类似水泡从水底浮出水面的过程,所以称这个过程为冒泡阶段。
针对这个过程,wilsonpage 做了一个 DEMO,可以非常直观地查看这个过程。
现在再看 addEventListener(eventName, handler, useCapture)
函数。第三个参数是 useCapture,代表是否在捕获阶段进行事件处理, 如果是 false, 则在冒泡阶段进行事件处理,如果是 true,在捕获阶段进行事件处理,默认是 false。这么设计的主要原因是当年微软和 netscape 之间的浏览器战争打得火热,netscape 主张捕获方式,微软主张冒泡方式,W3C 采用了折中的方式,即先捕获再冒泡。
4、事件委托
上面我们讲了事件的冒泡机制,我们可以利用这一特性来提高页面性能,事件委托便事件冒泡是最典型的应用之一。
何谓“委托”?在现实中,当我们不想做某件事时,便“委托”给他人,让他人代为完成。JavaScript 中,事件的委托表示给元素的父级或者祖级,甚至页面,由他们来绑定事件,然后利用事件冒泡的基本原理,通过事件目标对象进行检测,然后执行相关操作。看下面例子:
// HTML <ul id="list"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> <li>Item 4</li> <li>Item 5</li> </ul> // JavaScript var list = document.getElementById("list"); list.addEventListener("click", function(e) { console.log(e.target); });
上面的例子中,5 个列表项的点击事件均委托给了父元素 <ul id="list"></ul>
。
先看看事件委托的可行性。有人会问,当事件不是加在某个元素上的,如何在这个元素上触发事件呢?我们就是利用事件冒泡的机制,事件流到达目标元素后会向上冒泡,此时父元素接收到事件流便会执行事件执行程序。有人又会问,被委托的父元素下面如果有很多子元素,怎么知道事件流来自于哪个子元素呢?这个我们可以从事件对象中的 target
属性获得。事件对象下面会详细讲解。
我们再来看看为什么需要事件委托。
减少事件绑定。上面的例子中,也可以分别给每个列表项绑定事件,但利用事件委托的方式不仅省去了一一绑定的麻烦,也提升了网页的性能,因为每绑定一个事件便会增加内存使用。
可以动态监听绑定。上面的例子中,我们对 5 个列表项进行了事件监听,当删除一个列表项时不需要单独删除这个列表项所绑定的事件,而增加一个列表项时也不需要单独为新增项绑定事件。
看了上面的例子和解释,我们可以看出事件委托的核心就是监听一个 DOM 中更高层、更不具体的元素,等到事件冒泡到这个不具体元素时,通过 event 对象的 target 属性来获取触发事件的具体元素。
5、阻止事件冒泡
事件委托是事件冒泡的一个应用,但有时候我们并不希望事件冒泡。比如下面的例子:
const ele = document.getElementById("ele"); ele.addEventListener("click", function() { console.log("ele-click"); }, false); document.addEventListener("click", function() { console.log("document-click"); }, false);
我们本意是当点击 ele 元素区域时显示 "ele-click",点击其他区域时显示 "document-click"。但是我们发现点击 ele 元素区域时会依次显示 "ele-click" "document-click"。那是因为绑定在 ele 上的事件冒泡到了 document 上。想要解决这个问题,只需要加一行代码:
const ele = document.getElementById("ele"); ele.addEventListener("click", function(e) { console.log("ele-click"); e.stopPropagation(); // 阻止事件冒泡 }, false); document.addEventListener("click", function(e) { console.log("document-click"); }, false);
我们还能用 e.cancelBubble = true
来替代 e.stopPropagation()
。网上的说法是 cancelBubble
仅仅适用于 IE,而 stopPropagation
适用于其他浏览器。但根据我实验的结果,现代浏览器(IE9 及 以上、Chrome、FF 等)均同时支持这两种写法。为了保险起见,我们可以采用以下代码:
function preventBubble(e) { if (!e) { const e = window.event; } e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation(); } }
6、event 对象
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。当一个事件被触发的时候,就会创建一个事件对象。
我们用下面的代码打印出事件对象:
<div id="list"> <li>Item 1</li> <li>Item 2</li> </div> <script> var list = document.getElementById("list"); list.addEventListener("click", function(e) { console.log(e); }); </script>
chrome 49 的运行结果如下:
下面介绍一些比较常用的属性和方法。
target、 srcElement、 currentTarget 和 relatedTarget、fromElement、 toElement
target 与 srcElement 完全相同;
target refers to the element that triggered the event, currentTarget refers to the element to which the event is bound;
relatedTarget: the node related to the target node of the event. For mouseover events, this property is the node that the mouse pointer left when moving over the target node. For mouseout events, this property is the node that the mouse pointer enters when leaving the target.
This property is not useful for other types of events;fromElement and toElement are only valid for mouseover and mouseout events.
Using the above example, when <li>Item 1</li>
is clicked, the target is <li>Item 1</li>
element, and currentTarget is <p id="list"></p>
.
clientX/Y、 screenX/Y、 pageX/Y、 offsetX/Y
Above picture:
offsetX/Y: The click position is relative to the upper left corner of the element;
clientX/Y: The click position is relative to the upper left corner of the browser content area Position;
screenX/Y: The click position is relative to the upper left corner of the screen;
#pageX/Y: The click position is relative to the entire page The position of the upper left corner;
pageX/Y and clientX/Y will generally be the same, and will be different only when a scroll bar appears.
altKey, ctrlKey, shiftKey
altKey: Returns whether "ALT" is pressed when the event is triggered;
ctrlKey: Returns whether the "CTRL" key is pressed when the event is triggered;
shiftKey: Returns whether "SHIFT" is pressed when the event is triggered Whether the key is pressed;
Other attributes
type: Returns the name of the event represented by the current Event object
bubbles: Returns a Boolean value, indicating whether the event is a bubble event type;
cancelable: Returns a Boolean value, indicating whether the event can have a cancelable default action;
eventPhase: Returns the current stage of event propagation. There are three values: Event.CAPTURING_PHASE, Event.AT_TARGET, Event.BUBBLING_PHASE. The corresponding values are 1, 2, and 3, which represent the capture stage respectively. , normal event dispatching and bubbling phase;
path: node passed by during the bubbling phase;
method
preventDefault(): Notify the browser not to perform the default action associated with the event;
stopPropagation(): Prevent bubbling;
Recommended study: "JS Basic Tutorial"