「Hey!Scripting Cat!モジュール読み込みにしたらボタンのonclickで関数が実行できなった!どうすればいい?」
なるほど良くある話ですね。HTML の onclick 属性は、イベント発生時にグローバルスコープで評価されるため、モジュールスコープ内で定義された関数を直接参照しようとすると、その関数が見つからず実行できません。
じゃあonclickが使えなくなるのかと言えばそうではありません。
モジュール読み込みでボタンから関数を実行する方法
web上のjavascriptにはグローバル領域に直接スクリプトを読み込む方法とモジュール(module)で読み込む方法の主に二種類があります。今回の目標はボタンを押して関数を実行する事が目的です。
工程は2つあります「ボタンを押す」「関数実行」。これを実現する方法は2つ存在します。
1.window.領域に関数を持ってくる(非推奨)
今までの感覚を変更しないのであれば一番簡単です。変更するのは関数側です。
■js
function moduleFunction() {console.log('関数実行');}
window.moduleFunction = moduleFunction;
これだけです。htmlも変更不要で簡単ですね。
現在onclickが動かないというのはつまりグローバル領域に関数が居ない為です。
じゃあ何処にいるのかというとモジュールスコープ領域に居ます。
つまり、window.関数名に現在の関数名を代入すればモジュール内の関数をhtml側からonclickで実行できます。折角モジュール読み込みするのであれば(スコープを気にし始めたのなら)一応非推奨ではありますが他の関数等はモジュールにいるので今までの通りの方法「<script src="script.js"></script>」でただjsを読み込むだけより全然良いと思います。
onclickが出来ないならレガシーな通常読み込みに戻そうと考えてしまうより一旦はこの方法で良いと思います。モダンへの入り口は広い方がいい。
モジュールに移行する理由
個人や小規模なサイトであれば必ずしもモジュール読み込みの形式にする必要はありません。ただ、そもそもモジュールで読み込む方法が生まれたという事は理由が、つまり利点があるから実装されました。
最大の理由はおそらく名前の衝突です。グローバルスコープに全ての変数や関数があると衝突して正しく動かなくなる可能性高くなります。
<script type="module" ></script>で直接モジュール領域を作ったり
<script type="module" src="script.js"></script>モジュール読み込みを行うとグローバルスコープを侵さずに衝突しない領域を作ることができます。
次に個々に領域を分断した事でそれをつなぐインポートとエクスポートが便利だからです。
これも結局はスコープの制御がしやすいという話に帰結します。
2.addEventListenerでボタン待ちをする
さて、次はよりモダンな方法です。
まずはボタンにidを付けます(他にも方法はある後述)。
■html
<button id="mybutton">ボタン</button>
■js
function moduleFunction() {console.log('関数実行');}
document.getElementById("mybutton").addEventListener('click', moduleFunction)
これだけです。それほど難しくはなりません。
モジュールのトップレベルのコードは、モジュールが最初に読み込まれ、評価される際に一度だけ実行されます。このタイミングで addEventListener を呼び出すことで、ボタン要素にクリックイベントのリスナーが登録され、ボタンがクリックされるのを待機する状態になります。
これによってボタンが押されるとidで対象のボタンが特定されクリックが実行されたことでmoduleFunctionを実行できます。
少し注意点としては「addEventListener('click', moduleFunction)」の関数名部分(この場合「moduleFunction」)には「()」をつけてはいけません。
関数登録に括弧が付くとその位置で即時関数が実行されてしまいます。
※ボタンを特定する他の方法
id 以外に、nameやclassやタグ名で要素を取得できます。
・class(例: document.querySelector('.mybutton'))
・タグ(例: document.querySelectorAll('button'))
引数を渡したい場合
括弧が無ければじゃあ引数渡せないのかというとそうではなく
document.getElementById("mybutton").addEventListener('click', ()=>{moduleFunction(1)})
とすれば引数を渡す事が出来ます。
これでonclickとはさよならできますね。
関数に飛ばさず直接処理する場合
こうするだけでOK。
さらっと流してるアロー関数部分は前の記事を参照してください。(https://blackstraycatreboot.blogspot.com/2025/04/blog-post.html)
要するに先ほどアロー関数に関数名を記述してるのは無名関数にする事で処理領域を確保して、この領域で改めて関数にキックしてたわけです。
ですから、ここに関数を記述せずに処理を記述すればOK!
余談
ちなみに「addEventListener」は実行時にイベントオブジェクトを渡します。
この場合、'click' イベントに対するリスナーなので、event は クリックイベント に関する情報を保持します。
今回のclick イベントの場合はeventの型はMouseEvent オブジェクト。
MouseEvent は Event クラスの派生型で、クリック固有のプロパティ(例: マウス座標)を含みます。
function moduleFunction(arg, event) {
console.log(`引数: ${arg}`);
// イベントオブジェクトの詳細を確認
console.log(`クリックされた要素:`, event.target);
console.log('イベントオブジェクト:', event);
console.log('type:', event.type); // "click"
console.log('target:', event.target); // <button id="mybutton">...
console.log('clientX/Y:', event.clientX, event.clientY); // 例: 150, 200
console.log('ctrlKey:', event.ctrlKey); // 例: false
}
document.getElementById('mybutton').addEventListener('click', (event) => {
moduleFunction(1, event); // event オブジェクトを渡す
});
こういった情報取得も可能です。
ただまぁ今回は「ボタンを押す」「関数実行」が主題なので今回はこれで終了。
0 件のコメント:
コメントを投稿