onloadイベントとは?ページ読み込み時の処理を初心者向けに解説
JavaScriptのonloadイベントの基本的な使い方を初心者向けに解説。window.onload、DOMContentLoaded、addEventListenerの違いから実用的なサンプルコードまで詳しく説明します。
onloadイベントとは?ページ読み込み時の処理を初心者向けに解説
みなさん、Webページを作っていて「ページが完全に読み込まれてからJavaScriptを実行したい」と思ったことはありませんか?
「画像が全部読み込まれてからアニメーションを開始したい」 「DOM要素が準備できてからイベントを設定したい」
こんなことを実現したいと思ったことはありませんか?
この記事では、JavaScriptのonloadイベントについて初心者向けに詳しく解説します。 基本的な使い方から類似するイベントとの違い、実用的なサンプルコードまで、実際に使えるレベルまで理解できるように説明していきます。
onloadイベントって何のこと?
onloadイベントの基本概念
onloadイベントは、Webページのすべてのリソースが完全に読み込まれた時に発生するイベントです。
HTML、CSS、JavaScript、画像、外部ファイルなど、ページに必要なすべてのものが準備できた時点で実行されます。 ページの初期化処理や、すべてのコンテンツが利用可能になってから実行したい処理に使用されます。
// 基本的な使い方window.onload = function() { console.log('ページが完全に読み込まれました!'); // ここに初期化処理を書く};
上記のコードを実行すると、ページのすべてが読み込み完了した時点でメッセージが表示されます。
シンプルですが、とても重要な機能なんです。
onloadイベントの基本的な使い方
window.onloadでの設定方法
最もシンプルな方法から見ていきましょう。
// 方法1: 関数を直接代入window.onload = function() { console.log('ページ読み込み完了'); // DOM要素への操作 const button = document.getElementById('myButton'); button.textContent = '準備完了!'; // イベントリスナーの設定 button.addEventListener('click', function() { alert('ボタンがクリックされました'); });};
この方法では、無名関数をwindow.onload
に直接代入しています。
document.getElementById
でボタン要素を取得して、テキストを変更しています。
その後、クリックイベントを設定しています。
名前付き関数を使うこともできます。
// 方法2: 名前付き関数を使用function pageLoadHandler() { console.log('ページ読み込み完了'); initializeApp();}
window.onload = pageLoadHandler;
function initializeApp() { // アプリケーションの初期化処理 console.log('アプリケーション初期化中...');}
この方法だと、関数を再利用しやすくなります。
addEventListenerを使う方法(推奨)
より柔軟で推奨される方法がこちらです。
// addEventListener を使用(推奨)window.addEventListener('load', function() { console.log('ページ読み込み完了(addEventListener)'); // 複数の処理を登録可能 setupNavigation(); loadUserData(); initializeAnimations();});
// 複数のイベントリスナーを登録可能window.addEventListener('load', function() { console.log('2つ目の処理');});
function setupNavigation() { console.log('ナビゲーション設定中...');}
function loadUserData() { console.log('ユーザーデータ読み込み中...');}
function initializeAnimations() { console.log('アニメーション初期化中...');}
addEventListener
を使うメリットは、複数のイベントリスナーを登録できることです。
window.onload
だと、後から設定したものが前のものを上書きしてしまいます。
addEventListener
なら、複数の処理を並行して実行できます。
HTMLでの直接指定
HTML側で直接指定することもできます。
<!DOCTYPE html><html><head> <title>onloadイベントのテスト</title></head><body onload="handlePageLoad()"> <h1>ページ読み込みテスト</h1> <p id="status">読み込み中...</p> <script> function handlePageLoad() { console.log('HTMLのonload属性から実行'); document.getElementById('status').textContent = '読み込み完了!'; } </script></body></html>
<body>
タグのonload
属性で関数名を指定します。
この方法は簡単ですが、JavaScriptとHTMLが混在するので、大きなプロジェクトではあまり推奨されません。
onload vs DOMContentLoaded vs その他のイベント
各イベントの違いを理解しよう
似たようなイベントがいくつかあるので、違いを理解しておきましょう。
// DOMContentLoaded: HTMLの解析が完了した時点(画像等の読み込み前)document.addEventListener('DOMContentLoaded', function() { console.log('1. DOMContentLoaded: HTML解析完了'); console.log('時刻:', new Date().toLocaleTimeString());});
// load: すべてのリソースの読み込み完了window.addEventListener('load', function() { console.log('2. load: すべてのリソース読み込み完了'); console.log('時刻:', new Date().toLocaleTimeString());});
// beforeunload: ページを離れる前window.addEventListener('beforeunload', function(event) { console.log('3. beforeunload: ページを離れようとしています'); // 確認ダイアログを表示(必要な場合) // event.returnValue = '';});
// unload: ページを離れる時window.addEventListener('unload', function() { console.log('4. unload: ページを離れました');});
この順番で実行されます。
- DOMContentLoaded: HTMLの構造が完成した時点
- load: 画像やCSSなどすべてが読み込み完了した時点
- beforeunload: ユーザーがページを離れようとした時
- unload: ページが実際に閉じられる時
実際の読み込み順序を確認
実際にどの順番で実行されるか確認してみましょう。
<!DOCTYPE html><html><head> <title>イベント順序テスト</title> <script> console.log('HEAD内のスクリプト実行'); document.addEventListener('DOMContentLoaded', function() { console.log('DOMContentLoaded発生'); logEventTiming('DOMContentLoaded'); }); window.addEventListener('load', function() { console.log('load発生'); logEventTiming('load'); }); function logEventTiming(eventName) { const now = performance.now(); console.log(`${eventName}: ${now.toFixed(2)}ms経過`); } </script></head><body> <h1>イベント順序テスト</h1> <img src="large-image.jpg" alt="大きな画像"> <script> console.log('BODY内のスクリプト実行'); </script></body></html>
大きな画像があると、DOMContentLoadedとloadの間に時間差が生まれます。
この時間差を理解することで、適切なイベントを選択できるようになります。
使い分けのガイドライン
どのイベントを使うべきかの判断基準をご紹介します。
// DOM操作のみを行う場合(推奨)document.addEventListener('DOMContentLoaded', function() { // DOM要素への操作 const elements = document.querySelectorAll('.my-class'); elements.forEach(element => { element.addEventListener('click', handleClick); });});
// 画像サイズを取得する場合window.addEventListener('load', function() { // 画像の実際のサイズを取得 const images = document.querySelectorAll('img'); images.forEach(img => { console.log(`画像サイズ: ${img.naturalWidth} x ${img.naturalHeight}`); });});
// 外部ライブラリが必要な場合window.addEventListener('load', function() { // 外部ライブラリの初期化 if (typeof MyLibrary !== 'undefined') { MyLibrary.init(); }});
function handleClick() { console.log('要素がクリックされました');}
DOMContentLoadedは、DOM要素の操作だけなら十分です。
loadは、画像サイズの取得や外部ライブラリの初期化が必要な場合に使います。
実用的なサンプルコード集
画像ギャラリーの初期化
実際のWebサイトで使えるギャラリーシステムの例です。
class ImageGallery { constructor() { this.images = []; this.currentIndex = 0; // ページ読み込み完了後に初期化 window.addEventListener('load', () => this.init()); } init() { console.log('ギャラリー初期化開始'); // 画像要素を取得 this.images = Array.from(document.querySelectorAll('.gallery img')); if (this.images.length === 0) { console.warn('ギャラリー画像が見つかりません'); return; } // 各画像の読み込み状況をチェック this.checkImagesLoaded().then(() => { this.setupNavigation(); this.setupImagePreview(); console.log('ギャラリー初期化完了'); }); } async checkImagesLoaded() { const promises = this.images.map(img => { return new Promise((resolve, reject) => { if (img.complete) { resolve(); } else { img.onload = resolve; img.onerror = reject; } }); }); try { await Promise.all(promises); console.log('すべての画像の読み込み完了'); } catch (error) { console.error('画像の読み込みエラー:', error); } } setupNavigation() { const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); if (prevBtn) { prevBtn.addEventListener('click', () => this.previousImage()); } if (nextBtn) { nextBtn.addEventListener('click', () => this.nextImage()); } // キーボードナビゲーション document.addEventListener('keydown', (event) => { switch(event.key) { case 'ArrowLeft': this.previousImage(); break; case 'ArrowRight': this.nextImage(); break; } }); } setupImagePreview() { this.images.forEach((img, index) => { img.addEventListener('click', () => { this.showImage(index); }); }); } previousImage() { this.currentIndex = this.currentIndex > 0 ? this.currentIndex - 1 : this.images.length - 1; this.showImage(this.currentIndex); } nextImage() { this.currentIndex = this.currentIndex < this.images.length - 1 ? this.currentIndex + 1 : 0; this.showImage(this.currentIndex); } showImage(index) { // 画像表示ロジック console.log(`画像 ${index + 1} を表示中`); this.currentIndex = index; }}
// ギャラリーのインスタンス作成const gallery = new ImageGallery();
このクラスは、画像ギャラリーの完全な機能を提供します。
window.addEventListener('load')
を使うことで、すべての画像が読み込まれてから初期化処理を開始します。
キーボードナビゲーションやクリックイベントも設定されています。
データの非同期読み込みシステム
ページ読み込み後にデータを取得するシステムの例です。
class DataLoader { constructor() { this.data = null; this.isLoading = false; // ページ読み込み後にデータを取得 window.addEventListener('load', () => this.initializeData()); } async initializeData() { console.log('データ初期化開始'); try { this.showLoadingIndicator(); // 複数のデータソースから並列取得 const [userData, settingsData, contentData] = await Promise.all([ this.fetchUserData(), this.fetchSettings(), this.fetchContent() ]); this.data = { user: userData, settings: settingsData, content: contentData }; this.renderPage(); console.log('データ初期化完了'); } catch (error) { console.error('データ初期化エラー:', error); this.showErrorMessage('データの読み込みに失敗しました'); } finally { this.hideLoadingIndicator(); } } async fetchUserData() { // ユーザーデータの取得(シミュレーション) return new Promise(resolve => { setTimeout(() => { resolve({ name: 'ユーザー名', email: 'user@example.com' }); }, 1000); }); } async fetchSettings() { // 設定データの取得(シミュレーション) return new Promise(resolve => { setTimeout(() => { resolve({ theme: 'light', language: 'ja' }); }, 800); }); } async fetchContent() { // コンテンツデータの取得(シミュレーション) return new Promise(resolve => { setTimeout(() => { resolve({ articles: ['記事1', '記事2', '記事3'] }); }, 1200); }); } showLoadingIndicator() { const loader = document.getElementById('loading'); if (loader) { loader.style.display = 'block'; } } hideLoadingIndicator() { const loader = document.getElementById('loading'); if (loader) { loader.style.display = 'none'; } } showErrorMessage(message) { const errorDiv = document.getElementById('error'); if (errorDiv) { errorDiv.textContent = message; errorDiv.style.display = 'block'; } } renderPage() { if (!this.data) return; // ユーザー情報の表示 const userInfo = document.getElementById('userInfo'); if (userInfo) { userInfo.innerHTML = ` <h2>ようこそ、${this.data.user.name}さん</h2> <p>メール: ${this.data.user.email}</p> `; } // コンテンツの表示 const contentList = document.getElementById('contentList'); if (contentList) { contentList.innerHTML = this.data.content.articles .map(article => `<li>${article}</li>`) .join(''); } console.log('ページレンダリング完了'); }}
// データローダーのインスタンス作成const dataLoader = new DataLoader();
このクラスは、複数のデータソースから並列でデータを取得します。
Promise.all
を使うことで、すべてのデータ取得を効率的に実行できます。
ローディング表示やエラーハンドリングも含まれています。
アニメーション初期化システム
ページ読み込み後にアニメーションを開始するシステムです。
class AnimationController { constructor() { this.animations = []; this.isInitialized = false; // ページ読み込み完了後にアニメーション開始 window.addEventListener('load', () => this.initializeAnimations()); } initializeAnimations() { console.log('アニメーション初期化開始'); // 要素の準備状況をチェック if (!this.checkElementsReady()) { console.warn('アニメーション要素が準備できていません'); return; } this.setupFadeInAnimations(); this.setupScrollAnimations(); this.setupLoadingAnimations(); this.isInitialized = true; console.log('アニメーション初期化完了'); } checkElementsReady() { const requiredElements = [ '.fade-in', '.scroll-animate', '.loading-animation' ]; return requiredElements.every(selector => { const elements = document.querySelectorAll(selector); return elements.length > 0; }); } setupFadeInAnimations() { const fadeElements = document.querySelectorAll('.fade-in'); fadeElements.forEach((element, index) => { // 初期状態を設定 element.style.opacity = '0'; element.style.transform = 'translateY(20px)'; element.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; // 遅延してフェードイン setTimeout(() => { element.style.opacity = '1'; element.style.transform = 'translateY(0)'; }, index * 200); }); } setupScrollAnimations() { const scrollElements = document.querySelectorAll('.scroll-animate'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate'); } }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); scrollElements.forEach(element => { observer.observe(element); }); } setupLoadingAnimations() { const loadingElements = document.querySelectorAll('.loading-animation'); loadingElements.forEach(element => { // ローディングアニメーションを停止 element.classList.add('loaded'); // 完了アニメーションを開始 setTimeout(() => { element.classList.add('complete'); }, 500); }); } // 手動でアニメーションを開始 startAnimation(selector) { if (!this.isInitialized) { console.warn('アニメーションシステムが初期化されていません'); return; } const elements = document.querySelectorAll(selector); elements.forEach(element => { element.classList.add('animate'); }); }}
// アニメーションコントローラーのインスタンス作成const animationController = new AnimationController();
このクラスは、様々なタイプのアニメーションを管理します。
フェードインアニメーション、スクロールアニメーション、ローディングアニメーションを統合的に制御できます。
よくある問題と解決法
複数のonload設定による上書き問題
window.onload
を複数回設定すると、後から設定したものが前のものを上書きしてしまいます。
// ❌ 問題のあるコード:後から設定したものが前のものを上書きwindow.onload = function() { console.log('最初の処理');};
window.onload = function() { console.log('2番目の処理'); // この処理のみ実行される};
// ✅ 正しい解決法:addEventListenerを使用window.addEventListener('load', function() { console.log('最初の処理');});
window.addEventListener('load', function() { console.log('2番目の処理'); // 両方実行される});
addEventListener
を使うことで、複数の処理を同時に実行できます。
DOM要素が見つからない問題
DOMが読み込まれる前に要素を取得しようとするとエラーが発生します。
// ❌ 問題のあるコード:DOMが読み込まれる前に要素を取得しようとするconst button = document.getElementById('myButton'); // null になる可能性button.addEventListener('click', handleClick); // エラー
// ✅ 正しい解決法:onloadまたはDOMContentLoaded後に取得window.addEventListener('load', function() { const button = document.getElementById('myButton'); if (button) { button.addEventListener('click', handleClick); } else { console.warn('ボタン要素が見つかりません'); }});
function handleClick() { console.log('ボタンがクリックされました');}
要素の存在チェックを忘れずに行いましょう。
画像サイズ取得のタイミング問題
画像が読み込まれる前にサイズを取得しようとすると、正しい値が取得できません。
// ❌ 問題のあるコード:画像が読み込まれる前にサイズを取得document.addEventListener('DOMContentLoaded', function() { const img = document.querySelector('img'); console.log(img.width, img.height); // 0, 0 になる可能性});
// ✅ 正しい解決法:window.onloadまたは個別の画像loadイベントwindow.addEventListener('load', function() { const img = document.querySelector('img'); console.log('画像サイズ:', img.naturalWidth, img.naturalHeight);});
// または個別の画像に対してconst img = document.querySelector('img');img.addEventListener('load', function() { console.log('この画像のサイズ:', this.naturalWidth, this.naturalHeight);});
画像のサイズが必要な場合は、必ずload
イベント後に取得しましょう。
まとめ
JavaScriptのonloadイベントについて、基本から実践的な応用まで詳しく解説しました。
重要なポイントをまとめました。
- onloadはすべてのリソース読み込み完了後に発生
- DOMContentLoadedはHTML解析完了時点で発生(より早い)
- addEventListenerの使用が推奨(複数リスナー可能)
- 適切なイベントを選択することでパフォーマンス向上
実践での使い分け:
- DOM操作のみならDOMContentLoadedを使用
- 画像サイズ取得や外部リソース依存の処理はonloadを使用
- 複数の処理を登録する場合はaddEventListenerを使用
- エラーハンドリングと要素の存在チェックを忘れずに
onloadイベントを適切に活用することで、ユーザーエクスペリエンスの向上とパフォーマンスの最適化を両立したWebアプリケーションを構築できます!
まずは基本的なwindow.addEventListener('load')
から始めて、徐々に高度なテクニックを実践に取り入れてみてください。
きっと、より快適で機能的なWebサイトが作れるようになりますよ!