웹 브라우저는 HTML
을 해석(파싱)하는 과정 중 <script>
를 만나면 잠시 HTML
해석을 멈추고 <script>
요소를 먼저 실행한다. <script>
요소는 등장과 동시에 실행이 되기 때문에 HTML
내 위치에 따라 가끔 문제를 일으키기도 한다.
<script>
는 Body 태그가 끝나기 전에 넣자문제는 HTML
을 파싱하면서 DOM
트리를 생성해야만 javascript
로 DOM
을 오류없이 조작할 수 있는데 파싱이 트리가 생성되기 전에 <script>
가 실행이 되면 버그가 생길 수 있다. 따라서 스크립트의 위치는 Body
태그가 끝나기 전 위치에 넣는것이 옳다.
<script>
의 속성에는 async
와 defer
두 가지가 있다. <script>
를 Body
태그가 끝나기 전 위치에 넣는것이 옳지만 경우에 따라서는 스크립트를 먼저 실행해야 하는 경우도 생긴다. 해결책은 다음과 같다.
<script async src="script.js">
<script defer src="script.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>
</head>
<body>
<head>
<meta charset="UTF-8" />
</head>
<body>
<script>
window.onload = function () {
console.log("after window load");
console.log(document.querySelector("#div"));
};
document.addEventListener("DOMContentLoaded", function () {
console.log("after dom load");
console.log(document.querySelector("#div"));
});
console.log("script 실행 시작");
console.log(document.querySelector("#div"));
</script>
<div id="div">div</div>
</body>
</body>
</html>
위 코드는 script
태그가 div
태그 보다 위에 있다. 하지만 DOMContentLoaded & onload 를 이용하면 오류 없이 div
를 읽을 수 있다. 위 html
을 브라우저로 실행 시켰을 때 콘솔의 결과는 아래와 같다.
브라우저 동작 방식을 다시 설명하면 아래와 같다.
- HTML을 읽기 시작한다.
- HTML을 파싱한다.
- DOM 트리를 생성한다.
- Render 트리(DOM tree + CSS의 CSSOM 트리 결합)가 생성되고
- Display에 표시한다.
이 때 DOMContentLoaded
의 콜백은 DOM 트리가 생성이 끝난 후에 실행되고(3번 이후) (위 onload
의 콜백은 문서에 포함된 모든 콘텐츠(images, script, css, ...)가 전부 로드된 후 (4번 이후) 실행되는 차이가 있다.