JavaScript イベントの解釈
1. イベントの基本概念
イベント は、特定の Web ページを開くなど、ドキュメントまたはブラウザーで発生する特定の対話の瞬間を指します。ブラウザーが読み込まれた後に load
イベントがトリガーされ、hover がトリガーされます。マウスが要素の上に移動すると
イベント、マウスが要素をクリックすると click
イベントがトリガーされます。
イベント処理は、イベントがトリガーされたときのイベントに応答するブラウザの動作であり、この動作に対応するコードは イベント ハンドラです。
2. イベント操作: 監視と監視の削除
2.1 イベントの監視
ブラウザは、いくつかのイベントに基づいて、対応するイベント処理を実行します。イベントをリッスンするには、主に 3 つの方法があります。
2.1.1 HTML インライン属性
# は、HTML 要素にイベント関連の属性を直接入力することを意味し、属性値はイベント処理です。プログラム。例は次のとおりです。
<button></button>
onclick
は click
イベントに対応するため、ボタンがクリックされると、イベント ハンドラーが実行されます。コンソール出力 あなたをクリックしました!
。
ただし、この方法は HTML コードと JavaScript コードを結合するため、コードのメンテナンスに役立たないため、この方法はできるだけ避ける必要があることに注意する必要があります。
2.1.2 DOM 属性バインディング
DOM ノードのプロパティを直接設定してイベントとイベント ハンドラーを指定します。上記のコード:
const btn = document.getElementById("btn"); btn.onclick = function(e) { console.log("You clicked me!"); };
上記の例では、まず、 btn オブジェクトを取得し、このオブジェクトに onclick
属性を追加して click
イベントをリッスンします。この属性値はイベント ハンドラーに対応します。このプログラムは、DOM レベル 0 イベント ハンドラーとも呼ばれます。
2.1.3 イベント リスニング関数
標準のイベント リスニング関数は次のとおりです。
const btn = document.getElementById("btn"); btn.addEventListener("click", () => { console.log("You clicked me!"); }, false);
上記の例は、まずノードを表す btn オブジェクトを取得し、次に追加することを意味します。このオブジェクトにイベント リスナーが作成され、click
イベントが発生するとコールバック関数が呼び出され、コンソールに You clicked me!
が出力されます。 addEventListener
この関数には 3 つのパラメーター false が含まれています。3 番目のパラメーターの意味は、イベント トリガーの 3 つの段階の後で説明します。このプログラムは、DOM レベル 2 イベント ハンドラーとも呼ばれます。 IE9、FireFox、Safari、Chrome、Opera はすべて DOM レベル 2 イベント ハンドラーをサポートしています。IE8 以前の場合は、attacEvent()
関数を使用してイベントをバインドします。
これにより、互換性のあるコードを書くことができます:
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 イベント リスニングを削除します
イベントを要素にバインドした後、Binding にアクセスしたい場合は、 removeEventListener
メソッドを使用する必要があります。次の例を見てください:
const handler = function() { // handler logic } const btn = document.getElementById("btn"); btn.addEventListener("click", handler); btn.removeEventListener("click", handler);
バインドされたイベントのコールバック関数は匿名関数にすることはできませんが、宣言された関数でなければならないことに注意してください。これは、このコールバック関数の参照は、次の場合に渡す必要があるためです。イベントのバインドを解除します。
同様に、IE8 以下のバージョンは上記のメソッドをサポートしていませんが、代わりに detachEvent
を使用します。
const handler = function() { // handler logic } const btn = document.getElementById("btn"); btn.attachEvent("onclick", handler); btn.detachEvent("onclick", handler);
同様に、互換性のある削除イベント関数を作成できます:
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. イベント トリガー プロセス
イベント ストリームは、ページがイベントを受信する順序を記述します。最新のブラウザ (IE6 ~ IE8 以外のブラウザ (IE9、FireFox、Safari、Chrome、Opera などを含む) のイベント フロー) には、キャプチャ フェーズ、ターゲット フェーズ、バブリング フェーズという 3 つのプロセスが含まれます。は、このプロセスを示しています。
次に、これら 3 つのプロセスについて詳しく説明します。
3.1 キャプチャフェーズ
マウスのクリックやホバーなど、DOM 要素を操作すると、イベントが DOM 要素に送信されます。このイベントは Window から始まり、 docuemnt、html、bodyの順に子ノードを通過し、目的の要素に到達するまでWindowから目的の要素の親ノードに到達するプロセスをCaptureフェーズと呼びます。この時点ではまだターゲット ノードに到達していないことに注意してください。
3.2 ターゲット フェーズ
キャプチャ フェーズの終わりに、イベントはターゲット ノードの親ノードに到達し、最終的にターゲット ノードに到達し、ターゲット ノードでイベントをトリガーします。これが 目標段階 です。
イベントによってトリガーされるターゲット ノードは最下位のノードであることに注意してください。たとえば、次の例:
<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
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5
上面的例子中,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 はイベントをトリガーした要素を指し、currentTarget はイベントがバインドされている要素を指します。
AssociatedTarget: に関連するノードイベントのターゲットノード。マウスオーバー イベントの場合、このプロパティは、ターゲット ノード上を移動するときにマウス ポインタが離れたノードです。 Mouseout イベントの場合、このプロパティは、マウス ポインタがターゲットから離れるときに入るノードです。
このプロパティは、他のタイプのイベントには役に立ちません。fromElement と toElement は、mouseover イベントと Mouseout イベントでのみ有効です。
上記の例を使用すると、<li>Item 1</li>
をクリックすると、ターゲットは <li>Item 1</li>
要素、currentTarget は <p id="list"></p>
です。
clientX/Y、screenX/Y、pageX/Y、offsetX/Y
上の図:
- ##offsetX/Y: クリック位置は要素の左上隅を基準とします; <p></p>
- clientX/Y: クリック位置は左上隅を基準としますブラウザのコンテンツ領域の隅 位置; <p></p>
- screenX/Y: クリック位置は画面の左上隅を基準とします; <p></p>
- #pageX/Y: クリック位置はページ全体を基準とします。左上隅の位置は、<p></p>
- pageX/Y と clientX/Y は通常同じであり、次のようになります。スクロールバーが表示される場合のみ異なります。 <p></p> #altKey、ctrlKey、shiftKey
- altKey: イベントがトリガーされたときに「ALT」が押されたかどうかを返します。
- <p></p>#ctrlKey: イベントがトリガーされたときに「CTRL」キーが押されたかどうかを返します;
- <p></p>shiftKey: イベントがトリガーされたときに「SHIFT」が押されたかどうかを返します。キーが押された;
#その他の属性
type: 現在の Event オブジェクトによって表されるイベントの名前を返します
bubbles: イベントがバブル イベント タイプであるかどうかを示すブール値を返します;
cancelable: イベントがバブル イベント タイプを持つことができるかどうかを示すブール値を返します。キャンセル可能なデフォルト アクション;
eventPhase: イベント伝播の現在の段階を返します。値は 3 つあります: Event.CAPTURING_PHASE、Event.AT_TARGET、Event.BUBBLING_PHASE。対応する値は 1 です。 、2、および 3 は、それぞれキャプチャ ステージを表します。 、通常のイベントのディスパッチおよびバブリング フェーズ;
パス: バブリング フェーズ中に通過したノード;
method
preventDefault(): イベントに関連付けられたデフォルトのアクションを実行しないようにブラウザに通知します;
stopPropagation( ): バブリングを防ぐ;
-
推奨学習: "
JS Basic Tutorial