はじめに
javaScriptにおいて以下の違いがよく分からず模索していたところ、ようやく理解できたためメモ。
ファイル名などは適当です。ご愛嬌ということでお許しください。
<script></script>
を記述する位置による違いが分からなかった
・<script src="○○○"></script>
でHTML内にJSファイルを適用するという理解はあったものの、記述位置による違いがあるのか全く分からなかった。また、それを理解するような事例に出会わなかった。
(自分はパターンAが絶対的に正しいと思っていた。)
<!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 src="./javaScript.js"></script> <!-- ここに記述 -->
</head>
<body>
---(後略)---
<!DOCTYPE html>
<html lang="en">
---(前略)---
<body>
---(中略)---
<script src="./javaScript.js"></script> <!-- ここに記述 -->
</body>
</html>
DOMContentLoaded
の存在
次のソースコードに出会って先述の2パターンの違いを理解することが出来ました。
document.addEventListener('DOMContentLoaded', function(){
document.getElementById('id').addEventListener('change', function(){
alert(this.value);
} ,false)
}, false);
最初は(1)のような入れ子にする意味が分からず(DOMContentLoadedの意味も知らず)、
次の記述であっても「実装内容は変わらないのでは?」と思っていました。
document.getElementById('id').addEventListener('change', function(){
alert(this.value);
} ,false);
なぜかうまくいかなかった → 記述位置が原因!!
(2)の方で挙動を確認したところ、想定に反してエラーが出されて困りました。
/
├ selectAlert.html
└ selectAlert.js
<!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 src="./selectAlert.js"></script>
</head>
<body>
<form>
<label for="id">アラート値:</label>
<select id="id" name="name">
<option value="">---選択肢---</option>
<option value="A">選択肢A</option>
<option value="B">選択肢B</option>
<option value="C">選択肢C</option>
</select>
</form>
</body>
</html>
document.getElementById('id').addEventListener('change', function(){
alert(this.value);
} ,false);
ここでDOMContentListener
について調べていく中でコンテンツ本体(HTML部位)を読み込む前にDOMを取得しようとするとJSが空振る(失敗する) という事が分かりました。
じゃあ解決策は?
意識すべきは『HTML本体の読み込み』ということが分かったので次の対策が出来る事が分かりました。
A).jsの実装にdocument.addEventListener('DOMContentLoaded', function(){~});
を用いる
B).htmlの実装に</body>(閉じタグ)
の直前に<script src="○○○"></script>
を配置する
A).jsの実装にdocument.addEventListener('DOMContentLoaded', function(){~});
を用いる
'DOMContentLoaded'
は「イベントの種類」を意味しており、『HTMLのコンテンツを読み込み完了した』というイベントで発火する。
⇒ これにより「HTMLのコンテンツを読み込み完了するまでjsは処理を始動するのを待つ」という指定になる。
>>> 結果、読み込む前にDOMを取得しようとするとJSが処理を失敗するという状況が回避できました。
※'DOMContentLoaded'
には画像のロードは含まれないそうなので厳密には「『HTMLのコンテンツだけを読み込み完了した』というイベント」のが正確
A)パターンのサンプルコード
<!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 src="./selectAlert.js"></script>
</head>
<body>
<form>
<label for="id">アラート値:</label>
<select id="id" name="name">
<option value="">---選択肢---</option>
<option value="A">選択肢A</option>
<option value="B">選択肢B</option>
<option value="C">選択肢C</option>
</select>
</form>
</body>
</html>
'use strict';
document.addEventListener('DOMContentLoaded', function(){
document.getElementById('id').addEventListener('change', function(){
alert(this.value);
} ,false)
}, false);
B).htmlの実装に</body>(閉じタグ)
の直前に<script src="○○○"></script>
を配置する
こちらでは.htmlファイルの<script>~</script>
の記述位置を移動させることで、.jsファイルのロードを強制的に最後にする。
⇒ 「.html → .js」の順にロードをするように強制
>>> <script>~</script>
の記述位置で読み込み終えてからDOMを取得させることで回避させることが出来ました。
B)パターンのサンプルコード
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form>
<label for="id">アラート値:</label>
<select id="id" name="name">
<option value="">---選択肢---</option>
<option value="A">選択肢A</option>
<option value="B">選択肢B</option>
<option value="C">選択肢C</option>
</select>
</form>
<script src="./selectAlert.js"></script>
</body>
</html>
'use strict';
document.getElementById('id').addEventListener('change', function(){
alert(this.value);
} ,false);
所感
ここで疑問に思ったのは「<script>~</script>
を<head>内に含める
or <body>末尾に記述する
のどちらが好ましいのか」が気になりました。
どちらであっても対策次第で実装できるのであればどちらが良いのか判断が微妙だなと思いました。