今回は、日頃業務をしながら気になったことを勉強してみました。
HTML内にあるscriptの処理とデータの渡し方です!
この記事を通じて、JavaScriptのデータの渡し方やイベント制御のテクニックを習得に繋がればと思います。
まずはコードと仕様
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type='text/javascript'>
// 処理A
(async function() {
setTimeout(async ()=> {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
GLOBAL_DATA = data;
console.log('処理A', data);
}, 2000);
})();
</script>
</head>
<body>
<div id='hoge'>
<h1>hoge</h1>
</div>
<script type='text/javascript'>
// 処理B
const element = document.getElementById('hoge');
console.log('処理B', element, GLOBAL_DATA);
if (GLOBAL_DATA.hoge == false) {
element.style.display = 'none';
} else {
element.style.display = 'block';
}
</script>
</body>
</html>
このようなコードをどうしても書きたくなりました!
単純にいけば、処理Aを処理Bの中に入れてしまえばいいのですが仕様上、
データの取得がAの場所、
取得したデータを使ってBの場所で続きの処理
みたいに処理が2箇所に分かれることになりました。
Bの場所でデータを再取得することも可能でしたが、その場合処理が重くなってしまいます。
細かく話すと
・データの処理は早めに行いたい
・他の機能との関係上データ処理をAから動かせない
・要素の操作はHTMLが読み込まれてから行いたい
・要素の操作をAの処理の中に入れてしまうと、処理がぶつかってうまく動かない
大きなプロジェクトになってくると色々と工夫が必要になってきます。
単純に考えれば、headの処理Aの中で使用している即時関数の外側で、let GLOBAL_DATA を定義してあげて、処理Aの中で再代入することで処理B側に渡すことは可能です。
即時関数のブロックの壁を越えることは、特に難しくありません。
気になったのはそこではなくて…それよりも大きな壁が実はありました。
処理の順番をコントロールするには… new Event
思う通りに処理が流れてくれれば、AからBへ問題なくデータが渡りますが、
・body内のコードが少ない
・通信状況が悪いような環境
・取得データ量が多く時間がかかる
などその時の状況によって、処理Bには上手くデータが渡らないことがあるかと思います。
そこのコントロール、
処理A のfetchが終わってから、処理Bを発火させたい時。
そんな時に使えるのが、new Event と window.dispatchEvent です。
new Event と Window.dispatchEventについて
new Event
new EventはJavaScriptで新しいイベントオブジェクトを作成するためのものです。
このコマンドを使うことで、特定の名前を持つカスタムイベントを作成することができます。
カスタムイベントは、プログラム内の異なる部分で連携ができます
window.dispatchEvent
window.dispatchEventメソッドは、指定したイベントをターゲットオブジェクトに対して発火(実行)させるために使用されます。
このメソッドを使うことで、作成したカスタムイベントや、既存のDOMイベントを任意のタイミングで発火させることができます。
使い方の例
// カスタムイベント 'customEvent' を作成
var customEvent = new Event('customEvent');
// イベントリスナーを設定
window.addEventListener('customEvent', function(e) {
console.log('カスタムイベントが発火しました!');
}, false);
// 'customEvent' イベントを発火
window.dispatchEvent(customEvent);
このコードでは、最初にnew Eventを使ってcustomEventという名前の新しいカスタムイベントを作成しています。
次に、window.addEventListenerを使ってこのイベントが発火した時に実行される関数を設定しています。
最後に、window.dispatchEventを使ってcustomEventイベントを発火させ、コンソールにメッセージを表示しています。
ここまでの内容を最初のコードに入れ込むと
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type='text/javascript'>
let GLOBAL_DATA;
(async function() {
setTimeout(async ()=> {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
GLOBAL_DATA = data;
console.log('処理A', data);
// Data fetched event
const event = new Event('dataFetched');
window.dispatchEvent(event);
}, 2000);
})();
</script>
</head>
<body>
<div id='hoge'>
<h1>hoge</h1>
</div>
<script type='text/javascript'>
window.addEventListener('dataFetched', () => {
const element = document.getElementById('hoge');
console.log('処理B', element, GLOBAL_DATA);
if (GLOBAL_DATA.hoge == false) {
element.style.display = 'none';
} else {
element.style.display = 'block';
}
});
</script>
</body>
</html>
こうなります。
(処理Aが遅くなった時の状態を作るために、わざとsetTimeoutを組み込んであるので、そのままコードを使われる場合は外してください)
あと、ここまでやるともう一つ気になったのが、
・直接HTMLの中にscriptで書き込みをしてるが、別ファイルA.js,B.jsに分けた時にでも上手く動くのか?
も検証しましたが、意図した通りの動きが出来ることがわかりました。
まとめ
jsを書き始めた頃は、new Eventを見ても何をやってるか分からなかったあの頃。
これらからは、新しく使い方を覚えたことで一段といいものを作れるエンジニアに近づけたかと思います。
このやり方に感動を覚えたので、皆さんに共有です。
また何かいい情報があれば記事にしようと思います。
ARCHETYPからのお知らせ
ARCHETYPではShopifyテーマ、Shopifyアプリ、Webサイト/サービスなどの開発を行うエンジニアを募集中です!
正社員、副業などとわず募集です!(フルリモートも相談可です!)
https://www.archetyp.jp/recruit/
気になる方はお気軽にエントリーお待ちしております!