どうにかして、イベントハンドラにスコープ外の変数の値などを渡したいときがある。
色々方法があると思うが、最近bind()
を使ったらいいんじゃないかと思ったので紹介する。
他で紹介されているのを見ない気がするので、もしかしたら欠点があるのかもしれない。
bind()
を使う
bind()
の引数には、thisの値以外にも値が渡せるので、それを利用する。
Function.prototype.bind() - JavaScript | MDN
HTML
<button id="btn">Click!</button>
JavaScript
{
let value = "Hello!"; //スコープ外の変数
let btn = document.querySelector("#btn2");
let bindedHandler = handleClick.bind(btn, value); // bindにvalueの値を渡す
btn.addEventListener("click", bindedHandler);
}
//スコープ外にあるイベントハンドラ
function handleClick(val, event) {
console.log(typeof value); // undefined
console.log(val); // Hello!
event.preventDefault();
}
bindの2つ目以降に渡した引数は、新しく生成された関数が呼び出された際に、既存の引数の先頭に渡される。
通常、イベントハンドラにはeventオブジェクトが引数として渡されるので、bindに渡した引数はその前に来ることになる。
イベントハンドラの定義に引数が増えてしまうし、イベントオブジェクトが2番目以降に行ってしまうのでので、もう少し工夫したほうがいいかもしれない。
工夫した例
可変長引数をつかって引数の長さに応じて分岐させるようにしてみた。
冗長になったが、これならイベントハンドラを使い回すようなこともできる。
{
let value = "Hello!"; //スコープ外の変数
let btn = document.querySelector("#btn");
let bindedHandler = handleClick.bind(btn, value);
btn.addEventListener("click", bindedHandler);
}
//スコープ外にあるイベントハンドラ
function handleClick(...args){
let event,
len = args.length;
if (args.length > 1) {
event = args[len - 1];
args.forEach((e, i) => {
if (i + 1 !== len) {
console.log(e); //Hello!
}
});
}
else {
event = args[0];
}
event.preventDefault();
}
オーソドックスな方法
一応、bindを使わない、よくある方法も紹介する。
イベントハンドラを無名関数にして、イベントハンドラ内から別の関数を呼ぶ
{
let value = "Hello!"; //スコープ外の変数
let btn = document.querySelector("#btn");
btn.addEventListener("click", function(event) {
event.preventDefault();
anotherHandleClick(value);
});
}
anotherHandleClick(val) {
console.log(val); // Hello!
}
イベントハンドラを返す関数を呼ぶ
{
let value = "Hello!"; //スコープ外の変数
let btn = document.querySelector("#btn");
btn.addEventListener("click", anotherHandleClick(value));
}
anotherHandleClick(val) {
//イベントハンドラを返す
return function(event){
event.preventDefault();
console.log(val); // Hello!
};
}