JavaScriptのEvent

2022/07/12に公開

要素のEventをアクセス

まずは簡単に例を作ります

<button id="btn">Click Me!</button>
btn.addEventListener(
    "click", 
    function(){
        console.log('123')
    },
);

ブラウザのデバッグツールからEventを見るためには二つの方法があります

Element Panelから

まずは該当要素をセレクト

Side Panelから「Event Listeners」をセレクト

展開したら色々なOptionが見れます

Console Panelから

Console Panelに以下のコードを打ちます

getEventListeners(btn).click[0]

Eventが返してもらえました

addEventListenerの全てのパラメータ

これは全てのパラメータのデフォルトです

btn.addEventListener(
	"click", 
	function(){ console.log('123')},
	// options
	{
		once: false,
		passive: false, // 一部を除く
		capture: false,
	},
	// useCapture
	false 
);
  • 一つ目:Event Type、型はstring
  • 二つ目:Event Handler、型はfunction
  • 三つ目:Options、型はobject(記入内容は上を参照)
  • 四つ目:Use Capture、型はbool
    三つ目と四つ目のパラメータは記入しなくてもいいです。

ちなみに、三つ目の{capture: false}の効果は四つ目より強いです

つまり

btn.addEventListener(
	"click", 
	function(){ ... },
	{
		capture: true,
	},
	false 
);

このように、三つ目からcapturetrueを定義、四つ目はfalseを定義
三つ目の方が強いので、最終的にこのEventのuseCapturetrueとなります

一回きりのEvent

全てのパラメータをデフォルトのままだと
Eventは無数回にも受理されます

つまりボタンを何回クリックしても反応されます

でも、時々にはEventは一回きりに提供してほしい場合があります
そこで{once: true}を使ってこのようなものを簡単に作れます

btn.addEventListener(
	"click", 
	function(){ ... },
	{
+		once: true,
	},
	false 
);

これでボタンは一回クリックしたら、そのEventを捨てます
Console PanelでgetEventListeners(btn)を見ると、空きObjectが返してきます

Event Capture

まずは簡単な例を見てください

<nav>
	<ul>
		<li>Home</li>
		<li id="menuItem">
			<div>About US ▽</div>
			<ul id="menuChild" hidden>
				<li>Story</li>
				<li>Member</li>
				<li>Shop</li>
			</ul>
		</li>
		<li>Contact</li>
	</ul>
</nav>
html, body {
	margin: 0;
	height: 100vh;
}

nav > ul {
	background: beige;
	display: flex;
}

ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

ul li {
	padding: 20px;
}

ul li ul {
	position: absolute;
	background: antiquewhite;
}
menuItem.addEventListener(
	'click',
	function(e) {
		menuChild.hidden = !menuChild.hidden;
		console.log('menu event');
	},
	false
)

このように、簡単にドロップダウンを作ります

このドロップダウンには二つの特徴があります

  • アイテムをクリックすると、ドロップダウンが展開します
  • ドロップダウンが展開されている状態、再びアイテムをクリックすると、ドロップダウンを閉めます

そこで、既存の特徴をなくさないように、もう一つの特徴を加えたいです

  • ドロップダウンが展開されている状態、画面の任意の場所をクリックすると、ドロップダウンを閉めます

そして、bodyに対してクリックイベントを追加します

document.body.addEventListener(
    'click',
    function(e){
        document.querySelector('ul li ul').hidden = true;
        console.log('body event');
    },
    false
)

そして、問題です
「Abous US」を何回クリックしても、反応がしません

Console Panelを見てみると、以下のようにログが出ました

menu event
body event

その理由はEventが発生される順序です
「Abous US」に対して一回のクリックは

  1. Event Capture:body
  2. Event Capture:menuItem
  3. Event Bubble:menuItem、ログ「menu event」が発生
  4. Event Bubble:body、ログ「body event」が発生

発生順序は1.から4.までです

useCaptureのデフォルトはfalseなので、Event Capture段階は何も発生しません
(もしuseCapturetrueと設定する場合、ログの発生段階はEvent Captureに移します)

つまり、画面にはこんなことが発生ています

  1. 「Abous US」をクリックします
  2. menuItemのイベントが発生、ドロップダウンを展開します
  3. bodyのイベントが発生、ドロップダウンを閉めます

ドロップダウンは開けてすぐに閉めます、画面には何も発生してないように見えてきます

そして、こんな不具合を防ぐために、以下のように調整します

ボタンのクリックイベントは、bodyに伝わないようにします

e.stopPropagation()を加えます

menuItem.addEventListener(
	'click',
	function(e) {
+		e.stopPropagation();
		menuChild.hidden = !menuChild.hidden;
		console.log('menu event');
	},
	false
)

ちなみに、ドロップダウンをクリックするとドロップを閉めてしまいますので
以下のコードを加えます

menuChild.addEventListener(
    'click',
    function(e) {
        e.stopPropagation();
    },
    false,
)

そうすると動きが正常になりました

Discussion