見出し画像

【JavaScript】addEventListenerで関数に引数をわたす

JavaScriptでイベントバインド(クリックしたらこの関数が動く、というプログラム)するときには、『addEventListener』をつかうことが多いと思います。

今回は、「addEventListnerで呼び出す関数に『引数』をわたしたい!」という場面で引っかかりました。ググってもパッとわかる解説記事がなかったので、備忘録も兼ねて書いておきます。

・JavaScriptの基礎がわかる
・JavaScriptでアプリ開発をしたい
という方にむけて書いていきます。なお、jQueryやReactなどのライブラリは使わず、ネイティブJavaScriptでの解説記事になります。

ふつう、addEventListenerをつかって引数をわたしたければ、次のようなコードを書きたくなりますよね。

<script>

const userName = 'Ken';
const target = document.getElementById('start-button');

function sayHello(user){
    console.log('Hello, ' + user);
};

target.addEventListener('click',sayHello(userName));

</script>

start-buttonというidをもつ要素のクリックイベントにsayHelloという関数をひもづけ、userNameという定数を引数としてわたしています。

ところがこれではうまく動きません。理由はのちほど解説します。

addEventListenerに『handleEvent』というプロパティで関数をわたす

結論からいうと、以下のコードで解決することができます。

<script>

const userName = 'Ken';
const target = document.getElementById('start-button');

function sayHello(e){
    console.log('Hello, ' + this.name);
};

target.addEventListener('click', {name: userName, handleEvent: sayHello});

</script>

注目してほしいのは、addEventListenerの第2引数です。nameプロパティに引数、handleEventプロパティに実行する関数を指定した『オブジェクト』を渡しています。

sayHello関数ではthis.nameとすることで、わたってきた引数を参照することができます。

はじめのコードが動かない理由

まずaddEventListenerの基本的な構文は、以下です。

<script>

const userName = 'Ken';
const target = document.getElementById('start-button');

function sayHello(){
    console.log('Hello, ' + userName);
};

//OK
target.addEventListener('click', sayHello);

//NG
target.addEventListener('click', sayHello());
target.addEventListener('click', sayHello(name));

</script>

addEventListnerの第2引数には、「関数そのもの(=sayHello)」を指定しなければいけません。

つまり、sayHello()やsayHello(name)としても動きません。ちなみに、sayHello()をわたすと、『その関数の実行結果』がわたされてしまいます(はじめはぼくもハマりました)。

...でもこれじゃあ関数に引数わたせないよね、というのが、そもそもこの記事をかくきっかけとなった疑問です。

関数を引数として渡すのもイマイチ

もうひとつやりがちなのが、以下のような書き方です。

<script>

const userName = 'Ken';
const target = document.getElementById('start-button');

function sayHello(name){
    console.log('Hello, ' + name);
};

//関数をわたして、そこからターゲットの関数を引数つきで実行する
target.addEventListener('click', function(){
    sayHello(userName);
});

</script>

これでもおそらく動くにはうごきますが、イベントをアンバインド(解除)することができません。つまり、「1回クリックしたらsayHelloを実行して、2回目ではsayGoodbyeを実行する」みたいなことができなくなります。

それに、ある関数を実行するだけの関数を作るというのも気持ちがわるいですよね。

呼び出した関数内での『this』は第2引数を表す

サラッと書きましたが、この実装では、呼び出した関数内での『this』の扱いに注意が必要です。

ふつう、バインドした関数内でのthisは「クリックされた要素」を表します。じゃあ、実装コードのsayHello関数にあったthis.nameってなに?ってなりますよね。

addEventListenerに第2引数をわたした場合、関数内でのthisは「addEventListnerにわたした第2引数」を表します。ややこしいのでコードを見てください。

<script>

const userName = 'Ken';
const target = document.getElementById('start-button');

function sample(){
    //thisは「クリックされた要素」の情報をもつ
    console.log(this);
};

function sayHello(e){
    //thisはわたされた第2引数の情報をもつ
    console.log(this);
    
    //Hello, Kenと表示される
    //console.log('Hello, ' + this.name);
};

target.addEventListener('click', sample);
//<button id='start-button'></button>が表示される

target.addEventListener('click', {name: userName, handleEvent: sayHello});
//{name: userName, handleEvent: sayHello}が表示される
</script>

下から5行目がふつうのパターン。sample関数内でのthisは「クリックされた要素」をあらわします。

一方、引数つきで呼んだsayHelloではthisの挙動がかわり、「addEventListenerに第2引数として渡したオブジェクト」を表すようになります。なので、this.nameとすることで、引数としてわたしたuserNameが参照できるわけですね。

ちなみに、上記実装の関数内で、ふつうのthisみたいに「クリックされた要素」をとるには、e.currentTargetを使います。

<script>

const userName = 'Ken';
const target = document.getElementById('start-button');

function sayHello(e){
    //e.currentTargetで「クリックされた要素」をとる
    console.log(e.currentTarget);
    
    //Hello, Kenと表示される
    console.log('Hello, ' + this.name);
};

target.addEventListener('click', {name: userName, handleEvent: sayHello});

</script>

e.targetでもいいのですが、イベントバインドした要素(今回の例でいうとstart-btnというidをもった要素)に子要素があると意図した動きをしてくれないようなので、e.currentTartetとしたほうが無難なようです。

というわけで、addEventListenerに引数をわたすときは、引数と関数をオブジェクトにして渡してあげるといいよ、というお話でした。

この記事が気に入ったらサポートをしてみませんか?