タイトル: HTMLへの埋め込み
SEOタイトル: HTML に JavaScript を埋め込む完全ガイド
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong><script></strong> タグでインライン記述、<strong><script src></strong> で外部ファイル読み込み</li><li><strong>defer</strong>: HTML パース完了後・DOMContentLoaded 前に順次実行(推奨)</li><li><strong>async</strong>: ダウンロード完了次第すぐ実行、順序保証なし(独立スクリプト向け)</li><li><strong>type="module"</strong>: ES Modules、<code>import/export</code> 使用可、自動で defer 相当</li><li>DOM 操作前には <strong>DOMContentLoaded</strong> イベントを待つ、innerHTML は XSS 対策必須</li></ul></td></tr></table>
<h2>JavaScript を HTML に埋め込む 3 つの方法</h2>
<h3>1. インライン (script タグ内に直接記述)</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!DOCTYPE html>
<html>
<head>
<title>Sample</title>
</head>
<body>
<h1 id="title">Hello</h1>
<script>
// ここに JavaScript を直接書く
document.getElementById('title').textContent = 'Hello, World!';
console.log('inline script executed');
</script>
</body>
</html></code></pre>
<h3>2. 外部ファイル (推奨)</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- 同じディレクトリの app.js を読み込み -->
<script src="app.js"></script>
<!-- 絶対パス -->
<script src="/js/app.js"></script>
<!-- CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
<!-- 整合性ハッシュ + crossorigin(推奨セキュリティ設定) -->
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-..."
crossorigin="anonymous"></script></code></pre>
<h3>3. インライン属性 (非推奨)</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- ❌ 非推奨(XSS リスク、CSP 違反、保守性低い) -->
<button onclick="alert('clicked')">Click</button>
<!-- ✅ addEventListener 推奨 -->
<button id="btn">Click</button>
<script>
document.getElementById('btn').addEventListener('click', () => {
alert('clicked');
});
</script></code></pre>
<h2>script タグの属性 (defer / async / type)</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>属性</th><th>ダウンロード</th><th>実行タイミング</th><th>順序</th><th>用途</th></tr></thead>
<tbody>
<tr><td>なし</td><td>同期、HTML パース停止</td><td>即時</td><td>HTML 内記述順</td><td>互換性、最終手段</td></tr>
<tr><td><code>defer</code></td><td>非同期</td><td>HTML パース完了後、DOMContentLoaded 前</td><td>HTML 内記述順</td><td>★通常のアプリスクリプト</td></tr>
<tr><td><code>async</code></td><td>非同期</td><td>ダウンロード完了次第</td><td>不定</td><td>独立した解析タグ等</td></tr>
<tr><td><code>type="module"</code></td><td>非同期</td><td>defer 相当</td><td>記述順</td><td>ES Modules</td></tr>
</tbody>
</table>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!DOCTYPE html>
<html>
<head>
<!-- ★推奨: head に defer 付きで読み込み -->
<script src="app.js" defer></script>
<!-- 独立した広告/解析: async -->
<script src="https://www.googletagmanager.com/gtag/js?id=GA_ID" async></script>
<!-- ES Modules: 自動で defer 動作 -->
<script src="main.js" type="module"></script>
<!-- 旧ブラウザフォールバック: module 非対応で読まれる -->
<script src="main-legacy.js" nomodule></script>
</head>
<body>
<!-- body 末尾配置でも defer/async 不要だが、defer のほうが体感速い -->
</body>
</html></code></pre>
<h2>ES Modules</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- index.html -->
<script type="module" src="main.js"></script></code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// main.js
import { greet } from './utils.js'; // 拡張子必須
greet('World');
// utils.js
export function greet(name) {
console.log(`Hello, ${name}`);
}</code></pre>
<p>module は CORS / MIME (<code>application/javascript</code>) / 厳格モードが強制されるため、HTTP サーバ経由でアクセスする必要があります(<code>file://</code> 直接開きは不可)。</p>
<h2>DOM 操作の基本</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><div id="app">Hello</div>
<ul class="list">
<li>A</li>
<li>B</li>
</ul>
<script>
// 要素取得
const app = document.getElementById('app');
const items = document.querySelectorAll('.list li');
const first = document.querySelector('.list li:first-child');
// テキスト書き換え
app.textContent = 'World';
// HTML として埋め込み(XSS 注意)
app.innerHTML = '<strong>Bold</strong>';
// 属性
app.setAttribute('data-id', '42');
app.dataset.id = '42';
// クラス
app.classList.add('active');
app.classList.toggle('hidden');
// スタイル
app.style.color = 'red';
// イベント
app.addEventListener('click', (e) => {
console.log('clicked', e.target);
});
// 子要素追加
const li = document.createElement('li');
li.textContent = 'C';
document.querySelector('.list').appendChild(li);
</script></code></pre>
<h2>innerText / textContent / innerHTML の違い</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>プロパティ</th><th>HTML タグ</th><th>非表示要素</th><th>パフォーマンス</th><th>XSS</th></tr></thead>
<tbody>
<tr><td><code>textContent</code></td><td>取得・設定とも文字列扱い(エスケープ済)</td><td>含む</td><td>★高速</td><td>安全</td></tr>
<tr><td><code>innerText</code></td><td>表示テキストのみ</td><td>含まない</td><td>遅い(レイアウト必要)</td><td>安全</td></tr>
<tr><td><code>innerHTML</code></td><td>HTML として解釈</td><td>含む</td><td>遅い(パース必要)</td><td>★危険</td></tr>
</tbody>
</table>
<h2>XSS (クロスサイトスクリプティング) 対策</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// ❌ 危険: ユーザー入力を innerHTML に直接
const userInput = '<img src=x onerror="alert(1)">';
element.innerHTML = userInput; // → スクリプト実行される
// ✅ textContent で文字列扱い
element.textContent = userInput; // → "<img src=x..." と文字列表示
// ✅ どうしても HTML として描画したい場合は DOMPurify でサニタイズ
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
// ✅ 自前エスケープ関数(簡易)
function escapeHtml(str) {
return str.replace(/[&<>"']/g, m => ({
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
}[m]));
}
element.innerHTML = escapeHtml(userInput);</code></pre>
<h2>DOMContentLoaded と load イベント</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>イベント</th><th>発火タイミング</th><th>用途</th></tr></thead>
<tbody>
<tr><td><code>DOMContentLoaded</code></td><td>HTML パース + defer スクリプト完了時</td><td>★ DOM 操作開始</td></tr>
<tr><td><code>load</code></td><td>画像/CSS/iframe 含め全リソース完了</td><td>サイズ計算等、画像確認</td></tr>
</tbody>
</table>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">document.addEventListener('DOMContentLoaded', () => {
// ここから安全に DOM 操作可能
init();
});
window.addEventListener('load', () => {
// 画像 size などの最終確認
});
// 既に発火済みかチェック
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init(); // 既に発火後なら即実行
}</code></pre>
<h2>FAQ</h2>
<p><strong>Q: <code>defer</code> と body 末尾配置、どちらが良い?</strong><br />
A: 機能的にはほぼ同じですが、<code>defer</code> + head 配置のほうが「ダウンロードが HTML パースと並行して始まる」ぶん体感が速くなります。</p>
<p><strong>Q: <code>document.write</code> は?</strong><br />
A: 非推奨。HTML パース後に呼ぶとページ全体を破壊します。広告タグ等の互換目的以外は使わないこと。</p>
<p><strong>Q: jQuery の <code>$(function(){})</code> と <code>DOMContentLoaded</code> の違いは?</strong><br />
A: 同じです。jQuery 内部でも DOMContentLoaded を待つ実装になっています。</p>