Web API - 3 日目
イベントの進行について詳しく学び、よりインタラクティブな Web ページの特殊効果を実現し、イベント ストリームの特性を組み合わせてイベント実行の効率を最適化します。
- イベントの勃発を止める方法を学ぶ
- イベント委任の実装原則を理解する
イベントストリーム
イベント フローはイベントの実行プロセスを記述したもので、イベントの実行プロセスを理解することで、イベントへの理解が深まり、開発実践におけるイベントの使用の柔軟性が向上します。
上図にあるように、イベントが発生すると必ず「キャプチャステージ」と「バブリングステージ」の2つのステージを経ます。
つまり、捕獲段階は[父から息子へ]の伝達過程であり、バブリング段階は[息子から父へ]の伝達過程である。
キャプチャとバブル
イベント フローとは何かを理解した後、イベント フローがイベントの実行にどのような影響を与えるかを見てみましょう。
<body>
<h3>事件流</h3>
<p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
<div class="outer">
<div class="inner">
<div class="child"></div>
</div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer');
const inner = document.querySelector('.inner');
const child = document.querySelector('.child');
// html 元素添加事件
document.documentElement.addEventListener('click', function () {
console.log('html...')
})
// body 元素添加事件
document.body.addEventListener('click', function () {
console.log('body...')
})
// 外层的盒子添加事件
outer.addEventListener('click', function () {
console.log('outer...')
})
// 中间的盒子添加事件
outer.addEventListener('click', function () {
console.log('inner...')
})
// 内层的盒子添加事件
outer.addEventListener('click', function () {
console.log('child...')
})
</script>
</body>
上記のコードを実行すると、クリック イベントがトリガーされると、その先祖要素のクリック イベントも次々にトリガーされることがわかりました。これはなぜでしょうか?
イベント フローの特性と組み合わせると、要素のイベントがトリガーされると、イベントは常にその祖先を通過してから現在の要素に到達し、その後、現在の要素から祖先に渡されることがわかります。フロープロセス中にイベントが発生すると、イベントがトリガーされます。
さらに注目すべき点は、イベントが次々と発生する「実行順序」で、イベントの実行順序はキャプチャフェーズでもバブリングフェーズでも制御可能です。
イベントがバブリング ステージで実行される場合、それをバブリング モードと呼びます。最初に子ボックス イベントが実行され、次に親ボックス イベントが実行されます。デフォルトはバブリング モードです。
イベントがキャプチャフェーズで実行される場合、これをキャプチャモードと呼び、最初に親ボックスイベントを実行し、次に子ボックスイベントを実行します。
<body>
<h3>事件流</h3>
<p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
<div class="outer">
<div class="inner"></div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer')
const inner = document.querySelector('.inner')
// 外层的盒子
outer.addEventListener('click', function () {
console.log('outer...')
}, true) // true 表示在捕获阶段执行事件
// 中间的盒子
outer.addEventListener('click', function () {
console.log('inner...')
}, true)
</script>
</body>
結論は:
addEventListener
3 番目のパラメーターは、イベントがキャプチャ フェーズでトリガーされるかバブリング フェーズでトリガーされるかを決定します。addEventListener
3 番目のパラメータはtrue
キャプチャ フェーズ トリガーを示し、false
バブリング フェーズ トリガーを示します。デフォルト値は次のとおりです。false
- イベント フローは、親要素と子要素が同じイベント タイプを持つ場合にのみ効果があります。
- ほとんどのシーンではデフォルトのバブリング モードが使用されます (理由の 1 つは、初期の IE がキャプチャをサポートしていなかったことです)
泡立つのをやめる
バブリングの防止とは、イベントのフローをブロックして、イベントが現在の要素でのみ実行され、対応する祖先要素に影響を与えないようにすることを意味します。
<body>
<h3>阻止冒泡</h3>
<p>阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。</p>
<div class="outer">
<div class="inner">
<div class="child"></div>
</div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer')
const inner = document.querySelector('.inner')
const child = document.querySelector('.child')
// 外层的盒子
outer.addEventListener('click', function () {
console.log('outer...')
})
// 中间的盒子
inner.addEventListener('click', function (ev) {
console.log('inner...')
// 阻止事件冒泡
ev.stopPropagation()
})
// 内层的盒子
child.addEventListener('click', function (ev) {
console.log('child...')
// 借助事件对象,阻止事件向上冒泡
ev.stopPropagation()
})
</script>
</body>
結論: イベント オブジェクトのメソッドは、ev.stopPropagation
イベントのバブリングを防ぐために特に使用されます。
マウスオーバーイベント:
マウスオーバーとマウスアウトにはバブリング効果があります
Mouseenter と MouseLeave にはバブリング効果がありません (推奨)
イベントの代表団
イベント委任は、イベント ストリームの特性を使用して実際の開発ニーズを解決する知識とスキルであり、その主な機能はプログラムの効率を向上させることです。
次のコードに示すように、多数のイベント監視は比較的パフォーマンスに負荷がかかります。
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button');
for(let i = 0; i <= buttons.length; i++) {
// 为 10000 个 button 元素添加了事件
buttons.addEventListener('click', function () {
// 省略具体执行逻辑...
})
}
</script>
イベント ストリームの特性を使用して、上記のコードを最適化できます。イベントのバブリング モードでは、常にイベントがその親要素にフローされます。親要素が同じイベント タイプをリッスンする場合、親要素のイベントは次のコードに示すように、上記のコードを最適化するために使用されるのはこの機能です。
<script>
// 假设页面中有 10000 个 button 元素
let buttons = document.querySelectorAll('table button');
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
let parents = document.querySelector('table');
parents.addEventListener('click', function () {
console.log('点击任意子元素都会触发事件...');
})
</script>
私たちの最終的な目標は、ボタンのサブ要素がクリックされたときにのみイベントのコールバック関数が実行されるようにすることです。ユーザーがどのサブ要素をクリックしたかを判断するにはどうすればよいでしょうか?
イベント オブジェクト内の1 つtarget
または複数の属性srcElement
は、実際にイベントをトリガーした要素 (要素タイプのノード) を表します。
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button')
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
const parents = document.querySelector('table')
parents.addEventListener('click', function (ev) {
// console.log(ev.target);
// 只有 button 元素才会真正去执行逻辑
if(ev.target.tagName === 'BUTTON') {
// 执行的逻辑
}
})
</script>
最適化されたコードでは、祖先要素にイベント リスナーのみが追加されるため、10,000 個の要素にイベント リスナーを追加するよりもはるかに効率的です。!!
その他のイベント
ページ読み込みイベント
外部リソース (画像、外部 CSS、JavaScript など) が読み込まれるときにトリガーされるイベント。
何かを行うために、すべてのページ リソースが処理されるまで待つ必要がある場合があります。
イベント名:ロード
監視ページ上のすべてのリソースがロードされました。
window.addEventListener('load', function() {
// xxxxx
})
要素スクロールイベント
スクロールバーのスクロール中に継続的にトリガーされるイベント
window.addEventListener('scroll', function() {
// xxxxx
})
ページサイズイベント
ウィンドウ サイズが変更されるとイベントがトリガーされます。
window.addEventListener('resize', function() {
// xxxxx
})
要素のサイズと位置
要素自体によって設定された幅と高さ、パディング、境界線を含む、要素自体の幅と高さを取得します。
オフセット幅とオフセット高さ
得られるのは数値なので計算に便利です。
注: 取得されるのは表示されている幅と高さです。ボックスが非表示の場合、取得される結果は 0 です。
インチ事件
ウィンドウ サイズが変更されるとイベントがトリガーされます。
window.addEventListener('resize', function() {
// xxxxx
})
要素のサイズと位置
要素自体によって設定された幅と高さ、パディング、境界線を含む、要素自体の幅と高さを取得します。
オフセット幅とオフセット高さ
得られるのは数値なので計算に便利です。
注: 取得されるのは表示されている幅と高さです。ボックスが非表示の場合、取得される結果は 0 です。