HTML/CSS/Javascript

[Do it! HTML+CSS+자바스크립트 웹 표준의 정석] 17 (완)

EunaSon 2023. 8. 14. 19:13

17-1 문서 객체 모델 (DOM)

  1. 문서 객체 모델 알아보기

  2. DOM 요소에 접근하고 속성 가져오기

 

문서 계층 구조

자바스크립트에서는 프로그래밍 할수있는 모든 대상을 객체화함

웹 문서의 요소들을 프로그래밍하기 위해서는 각각의 요소들을 객체화시켜야하고, 이를 문서 객체 모델이라 함

문서 객체 모델(DOM)

: 자바스크립트를 이용하여 웹 문서에 접근하고 제어할 수 있도록 객체를 사용해 웹 문서를 체계적으로 정리하는 방법

 

예) 웹 문서 전체는 document 객체, 삽입한 이미지는 image 객체로 관리함

 

웹 문서의 요소(태그)들은 다음과 같이 요소의 계층 관계로 나타낼 수 있다

이 요소(태그)들만으로는 웹문서를 다 이해할 수 없음 

-> 각 태그의 속성, 태그가 감싸고 있는 텍스트의 내용까지 포함해서 구성하는 것이 DOM 트리임 

 

DOM 트리

웹 문서에 있는 요소들 간의 부모, 자식 관계를 계층 구조로 표시한 것

나무 형태가 되기 때문에 DOM 트리라고 함

노드(node) : DOM 트리에서 가지가 갈라져 나간 항목

루트 노드(root node) : DOM 트리의 시작 부분(html)

 

DOM을 구성하는 원칙

모든 html 태그요소 노드(element node) - html, head, body, meta, title, h1, img

웹 문서의 텍스트 내용요소 노드의 자식 노드텍스트 노드(text node) - Do it!

태그의 속성요소 노드의 자식 노드속성 노드(attribute node) - charset="...", src="...", alt="..."

주석주석 노드(comment node)

 

웹 문서의 내용은 다음과 같이 DOM 트리로 구성할 수 있음

 

meta의 속성인 charset="..."은 요소 노드인 meta의 자식 노드인 속성 노드 

h1이라는 요소 노드에 텍스트 노드인 Do it! 이 자식 노드로 연결됨

 

DOM 요소에 접근하기

웹 문서에서 원하는 요소를 찾아가는 것을 '접근한다(access)' 고 함

접근->요소를 가져와서->변수로 저장->변수에 여러 메서드, 프로퍼티를 사용해 원하는 프로그래밍을 하게 됨

 

웹 요소에 접근할 때는 두 가지 형태의 메서드를 사용하게 됨

 

1)

getElementById() 메서드

  [기본형] 요소명.getElementById("id명")

getElementsByClassName() 메서드

  [기본형] 요소명.getElementsByClassName("class명")

getElementsByTagName() 메서드

  [기본형] 요소명.getElementsByTagName("태그명")

 

: 자바스크립트 초기부터 사용하던 메서드들임

이 메서드들은 웹 요소에 접근하려 할때 접근할 수단(id값, 클래스명, 태그명)에 따라 선택하고, 수단에 따라 각각의 메서드를 따로 사용해주어야 함

 

class선택자는 여러번 사용 가능하므로 element's'

마찬가지로 같은 태그가 여러번 반복 사용될 수 있으므로 element's'

둘은 반환값이 2개 이상일 수 있고, 이 값들은 HTMLCollection 객체에 저장됨, 인덱스를 통해 각 값들에 접근할 수 있음 

 

2)

querySelector() 메서드, querySelectorAll() 메서드

[기본형]

노드.querySelector(선택자)

노드.querySelectorAll(선택자 또는 태그)

 

: 이 두 메서드들은 1)의 메서드들과 달리 웹 요소에 대한 접근 수단에 관계없이 사용할 수 있음

id명으로 접근하고 싶다면 해시 기호 # 를, 클래스명으로 접근하고 싶다면 앞에 마침표 . 를, 태그로 접근하고 싶다면 기호 없이 태그명을 써주면 됨

 

querySelector()는 한 개의 값만 반환

querySelectorAll()은 반환값이 여러개일 때 모두 반환->노드 리스트(배열과 비슷, 인덱스로 저장 및 접근)로 저장됨

 

클래스나 태그는 반환하는 값이 여러개 일 수 있음. 이때 querySelector()를 사용하면 반환값이 하나뿐이므로 여러 요소 중 첫번째 요소만 가져오게 됨. 따라서 모든 요소에 접근하고 싶다면 querySelectorAll()을 사용하면 됨

 

 

DOM 요소에 접근하고 속성 가져오기

getAttribute() 메서드 : 속성 노드의 값을 가져옴

  [기본형] getAttribute("속성명")

setAttribute() 메서드 : 속성 노드의 값을 바꿈

  [기본형] setAttribute("속성명", "바꿔넣을 값")

 

특히 image 같은 경우는 속성을 많이 사용하게 됨

<img sre="images/coffe-pink.jpg" alt="에디오피아 게뎁" id="cup" width="200" height="200" onclick="displaySrc()">

->

이미지 파일의 경로를 알고 싶을때 :

var cup = document.querySelector("#cup"); // id = "cup" 인 요소에 접근

cup.getAttribute("src"); // cup의 src 속성 값을 가져옴

 

setAttribute()를 통해 이미지 파일의 경로를 바꿔주어 다른 이미지로 변경할 수도 있음


17-2 문서 객체 모델 (DOM)

  3. DOM에서 이벤트 처리하기

 

이벤트 처리하기- 방법 1

이미지 클릭하면 알림 창이 뜨도록 처리해보자

보통 태그의 id값과 자바스크립트에서 변수 이름을 같이 해주는 경우가 많음

(함수 미리 정의x, 정의하며 즉시 실행)

변수.on이벤트 = function() { 실행할 명령; }

<body>
    <div id="container">
        <img src="images/cup-1.png" id="cup">
    </div>
    
    <script>
        var cup = document.querySelector("#cup"); // 변수로 지정
        cup.onclick = function() { // on이벤트 = 이벤트 처리 함수
            alert("이미지를 클릭했습니다.");
        }
    </script>
</body>

 

이벤트 처리하기- 방법 2 : 미리 함수를 만들어놓고, 그 함수를 연결해서 사용

이미지를 클릭했을 때 다른 이미지로 변경하는 함수 작성

(함수를 미리 정의해놓았을 때)

변수.on이벤트 = 정의한함수이름;

<body>
    <div id="container">
        <img src="images/cup-1.png" id="cup">
    </div>
    
    <script>
        var cup = document.querySelector("#cup"); //변수 지정, 이벤트에 정의한 함수 연결 
        cup.onclick = changePic; // changePic 뒤에 괄호 () 붙이지 않음 주의
        
        function changePic() {
            cup.src = "images/cup-2.png"; // 변수 cup의 src속성을 바꿔줌
        }
    </script>
</body>

 

예약어 this

이벤트가 발생한 대상에 접근하고 싶을 때 예약어 this를 사용함

<body>
    <div id="container">
        <img src="images/cup-1.png" id="cup">
    </div>
    
    <script>
        var cup = document.querySelector("#cup");
        cup.onclick = function() { 
            alert("현재 이미지 파일 경로는 " + this.src); // this는 click이벤트가 일어난 cup을 말함
        }
    </script>
</body>

여기서 말하는 this는 click이벤트가 발생한 대상인 cup(= id가 cup = img 태그)

 

이벤트 처리하기- 방법 3 : addEventListener() 메서드 사용하기

addEventListener() 메서드

[기본형] 요소.addEventListener(이벤트, 함수, 캡쳐여부);

이벤트 : 이벤트 유형을 지정함. 단, 이벤트 앞에 on을 붙이지 않고 사용함

함수 : 이벤트가 발생하면 실행할 명령이나 함수를 지정함. 여기서 함수를 정의할 때는 event객체를 인수로 받음

event 객체는 이 이벤트가 어떤 요소에서 발생했는지 또는 어떤 이벤트가 발생했는지, 이벤트가 발생한 위치가 어딘지 등의 정보를 가지고 있음 

캡쳐여부 : 이벤트를 캡쳐하는지 여부를 지정함=부모요소와 자식 요소가 있을 때, 부모요소에서 발생한 이벤트가 자식요소에게 전달되는지 혹은 자식요소에서 발생한 이벤트가 부모요소에게 전달되는지를 의미함

기본값은 false(버블링, 자식->부모로 전달)이고 true와 false 중에서 선택할 수 있음.

true이면 캡쳐링, false이면 버블링한다는 의미임.

이벤트 캡쳐링은 DOM의 부모 노드에서 자식 노드로 전달되는 것이고, 이벤트 버블링은 DOM의 자식 노드에서 부모 노드로 전달되는 것임

 

예)

화면에 나타난 이미지(easys-1.jpg)에 마우스를 올리면 다른 이미지(easys-2.jpg)로 변경, 마우스를 밖으로 내리면 원래 이미지(easys-1.jpg)로 돌아오도록 하자

<body>
    <div id="container">
        <img src="images/easys-1.jpg" id="cover">
    </div>
    
    <script>
        var cover = document.getElementById("cover");
        cover.addEventListener(mouseover, changePic, false); // false는 생략가능
        cover.addEventListener(mouseout, originPic, false);
        
        function changePic() {
            cover.src="images/easys-2.jpg";
        }
        
        function originPic() {
            cover.src="images/easys-1.jpg";
        }
    </script>
</body>

 

이벤트 처리기로 사용할 함수를 미리 정의하지 않고 addEventListener() 안에서 바로 정의하여 사용할 수도 있음

        cover.addEventListener(mouseout, function() { // 함수를 미리 정의하지 않고도 바로 사용가능 
            cover.src = "images/easys-1.jpg";
        });

 

DOM에서 스타일 다루기

DOM에서 css 스타일에 접근하거나 스타일 값을 변경할 수도 있음

<head>
    <style>
        #rect { <!-- 사각형을 만듦 -->
            width:100px;
            height:100px;
            border:1px solid #222;
            margin:30px auto;
            transition:1s;
        }
    </style>
</head>
<body>
    <div id="rect"></div>
    
    <script>
        var myRect = document.querySelector("#rect");
        myRect.addEventListener("mouseover", function() { // mouseover이벤트 처리
            myRect.style.backgroundColor = "green"; // myRect요소의 배경색
            myRect.style.borderRadius = "50%"; // myRect 요소의 테두리 둥글게 처리
        });
        myRect.addEventListener("mouseout", function() { // mouseout 이벤트 처리
            myRect.style.backgroundColor = ""; // myRect 요소의 배경색 지우기
            myRect.style.borderRadius = ""; // myRect 요소의 테두리 둥글게 처리 안함
        });
    </script>
</body>

변수.style.backgroundColor => 요소의 css style에서 background-color 속성을 바꿀 수 있음(하이픈 없애고 뒷단어 첫글자 대문자로)

마찬가지로, css의 border-radius 속성을 자바스크립트에서는 style.borderRadius 로 변경할 수 있음

 

여기서는 요소에 마우스를 올리면(이벤트) 테두리가 둥글어지며 배경색이 초록색이 되도록 지정,

마우스를 내리면 원래대로 돌아오도록 처리했는데

style에서 transition을 1초 지정했기 때문에 천천히 바뀌고 천천히 돌아오는 것을 볼 수 있음


17-3 문서 객체 모델 (DOM)

  4. Dom에서 노드 추가, 삭제하기

 

쇼핑몰 등 사이트에서 크기 등 옵션을 선택하면 하단에 원래는 보이지 않던 항목이 나타나는 것을 볼 수 있음

이와같이 기존에 보이지 않던 내용이 나타나거나, 삭제하면 추가되었던 노드가 사라지게 할 수 있음

웹문서에서는 하나하나의 요소를 노드로 관리하므로 노드에 접근해서 삭제, 추가할 수 있음

 

텍스트 내용이 있는 노드 추가하기

예) '더보기' 링크를 클릭하면 텍스트 표시하기 => 텍스트 노드 추가

h1을 이용한 제목 텍스트('DOM을 공부합시다')와

a 태그를 이용한 '더보기' 라는 링크, id값이 'info'인 div 태그가 있는 문서.

더보기를 클릭하면 기존에는 없었던 텍스트 한줄이 화면에 나타남

 

<div id="container">
    <h1>DOM을 공부합시다</h1>
    <a href="#" onclick="addP(); this.onclick'';">더보기</a> 
    <!-- 위에서 this.onclick='' 이 없으면 링크를 클릭할 때마다 계속 addP() 함수가 실행됨-->
    <div id="info"></div>
</div>

<script>
    function addP() {
        var newP = document.createElement("p");
        var txtNode = document.createTextNode("DOM은 document object model의 줄임말입니다");
        newP.appendChild(txtNode);
        document.getElementById("info").appendChild(newP);
    }
</script>

 

1. 요소 노드 만들기 - createElement() 메서드 : DOM트리 상에서 요소 노드가 일단 하나 만들어짐

[기본형] document.createElement(노드명)

 

우리는 텍스트를 표시할 것이기 때문에 p 요소노드를 만들것임.

=> var newP = document.createElement("p");

 

2. 텍스트 노드 만들기 - createTextNode() 메서드 : DOM트리 상에서 텍스트 노드도 따로 하나 만들어짐

[기본형] document.createTextNode(텍스트)

 

요소에서 텍스트 내용은 텍스트 노드에 따로 저장되므로 텍스트 노드를 메서드를 이용해 만들어줌

=> var txtNode = document.createTextNode("DOM은 document object node의 줄임말입니다.");

 

3. 자식 노드 연결하기 - appendChild() 메서드

[기본형] 부모노드.appendChild(자식노드)

 

텍스트 노드는 요소 노드의 자식노드임 => newP.appendChild(txtNode);

p 요소노드는 내가 p요소를 표시해주려고 하는 요소인 div(id값이 info)의 자식노드로 추가

=> document.getElementById("info").appendChild(newP);

 

 

=> a 태그인 '더보기' 링크 클릭시 addP() 함수 실행, addP()는 요소노드, 텍스트노드를 만들고 자식노드로 연결 후 p 요소 노드를 div의 자식노드로 연결하여 p태그의 텍스트가 화면에 표시되게 됨

 

속성과 속성 값이 있는 노드 추가하기

텍스트가 아니라 속성값이 있는 이미지 등을 삽입하는 경우를 생각해보자

이때는 속성 노드도 함께 만들어줘야 함

 

예) '더보기' 링크를 클릭하면 텍스트와 함께 이미지 표시하기

 

1. 요소 노드 만들기 - createElement() 메서드

이미지를 삽입하기 위해 img 요소 노드를 생성함

=> var newImg = document.createElement("img");

 

2. 속성 노드 만들기 - createAttribute() 메서드

[기본형] document.createAttribute(속성명)

img 태그는 여러 속성(파일 경로 및 대체텍스트)을 가지므로 속성노드도 만들어주어야 함

속성노드에 value 프로퍼티를 이용해 속성값을 지정해줌

=>

var srcNode = document.createAttribute("src");

var altNode = document.createAttribute("alt");

srcNode.value = "images/dom.jpg"; // src 속성값 지정

altNode.value = "돔 트리 예제 이미지"; // alt 속성값 지정

 

3. 속성 노드 연결하기 - setAttributeNode() 메서드

[기본형] 요소명.setAttributeNode(속성노드)

=>

img 요소노드에 src, alt 속성 노드 연결하기

newImg.setAttributeNode(srcNode);

newImg.setAttributeNode(altNode);

 

4. 자식 노드 연결하기 - appendChild() 메서드

img 노드도 연결해주어야 함

=>

document.getElementById("info").appendChild(newImg);

 

노드 삭제하기

노드를 삭제할 때는 자신을 스스로 삭제할 수는 없고, 부모노드에서 자식노드를 삭제해야함

-> 노드를 삭제하려면 부모노드부터 찾아야 함

 

parentNode 프로퍼티

: 현재 노드의 부모 노드에 접근하여 부모노드의 요소 노드를 반환함

  [기본형] 노드.parentNode

 

removeChild() 메서드

: 자식 노드를 삭제함

  [기본형] 부모노드.removeChild(자식노드)

 

예)

텍스트필드에 값을 입력하면 필드 아래쪽에 입력한 내용이 표시되도록 하자

값을 입력 > 엔터 > 하단에 입력한 값이 줄줄이 표시됨

텍스트노드 추가하는 방법을 이용함

<body>
    <div id="container">
        <h1>Web Programming</h1>
        <p>공부할 주제를 기록해보세요</p>
        <form action="">
            <input type="text" id="subject" autofocus>
            <button>추가</button>
        </form>
        <hr>
        <ul id="itemList"></ul>
    </div>
</body>

form 태그를 이용해서 input 텍스트필드 안에 내용을 입력할 수 있게 되어있음

텍스트필드의 id값은 subject임

표시할 내용은 ul 태그를 이용해서 목록형태로 표시할 것임

 

=>

우리가 만들 요소노드는 li가 됨(텍스트를 하나하나 입력할 때마다 li가 하나씩 추가되는 것임)

표시할 텍스트 내용은 텍스트필드에 입력된 값을 가져와서 텍스트노드로 사용할것임

 

<body>
    <div id="container">
        <h1>Web Programming</h1>
        <p>공부할 주제를 기록해보세요</p>
        <form action="">
            <input type="text" id="subject" autofocus>
            <button onclick="newRegister(); return false;">추가</button>
        </form>
        <hr>
        <ul id="itemList"></ul>
    </div>
    
    <script>
        function newRegister() { // '추가' 버튼을 누르면 실행될 함수
            var newItem = document.createElement("li");
            var subject = document.querySelector("#subject");
            var newText = document.createTextNode(subject.value); //subject.value=텍스트필드 안의 값
            
            newItem.appendChild(newText);
            document.querySelector("#itemList").appendChild(newItem);
        }
    </script>
</body>

<button onclick="newRegister();"> 까지만 입력하게되면, 텍스트필드에 입력한 내용이 필드 하단에 잠깐 나타났다가 사라져버림. 왜냐하면 이 버튼의 역할이 계속 지속되고있기 때문=> 버튼의 역할을 취소시켜줘야 함(return false;)

 

button이나 input 타입의 submit 등은 실제 자기의 역할 말고 다른 기능을 하도록 하기 위해서는 return false를 사용해주어야 함

 

그러나 텍스트필드에 입력해준 값이 엔터를 치고 나서고 그대로 남아있는 문제가 있음

=> 함수 마지막에 subject.value = ""; 를 추가해줌

<body>
    <div id="container">
        <h1>Web Programming</h1>
        <p>공부할 주제를 기록해보세요</p>
        <form action="">
            <input type="text" id="subject" autofocus>
            <button onclick="newRegister(); return false;">추가</button>
        </form>
        <hr>
        <ul id="itemList"></ul>
    </div>
    
    <script>
        function newRegister() { // '추가' 버튼을 누르면 실행될 함수
            var newItem = document.createElement("li");
            var subject = document.querySelector("#subject");
            var newText = document.createTextNode(subject.value); //subject.value=텍스트필드 안의 값
            
            newItem.appendChild(newText);
            document.querySelector("#itemList").appendChild(newItem);
            
            subject.value = "";
        }
    </script>
</body>

 

이제 추가해준 값을 삭제해보자(항목 클릭->삭제되도록)

추가해준 요소의 부모요소를 찾아가서 삭제해야함

 

일단 삭제하기 위해서는 화면에 표시된 항목들을 클릭하는 요소(여기서는 li)가 있는지 살펴보자

텍스트필드에 입력하여 화면에 나타난 값들을 items에 넣음,

items의 i번째 항목에 click이벤트가 발생하면

-> 해당 항목(li)의 부모요소가 있는지 if문 통해 확인

-> 부모요소가 있다면,

해당요소.부모요소.removeChile(해당요소)

로 클릭한 해당 요소 삭제함

 

<body>
    <div id="container">
        <h1>Web Programming</h1>
        <p>공부할 주제를 기록해보세요</p>
        <form action="">
            <input type="text" id="subject" autofocus>
            <button onclick="newRegister(); return false;">추가</button>
        </form>
        <hr>
        <ul id="itemList"></ul>
    </div>
    
    <script>
        function newRegister() { // '추가' 버튼을 누르면 실행될 함수
            var newItem = document.createElement("li");
            var subject = document.querySelector("#subject");
            var newText = document.createTextNode(subject.value); //subject.value=텍스트필드 안의 값
            
            newItem.appendChild(newText);
            document.querySelector("#itemList").appendChild(newItem);
            
            subject.value = "";
            
            var items = document.querySelectorAll("li"); // 추가해준 li 항목들을 다 찾음
            for(let i = 0; i < items.length; i++) {
                // 클릭하는 항목이 있을 때 함수(항목을 삭제함)를 실행하도록 할 것
                items[i].addEventListener("click", function(){
                    if(this.parentNode) { //items[i]에 부모노드가 있을때만 실행
                        this.parentNode.removeChild(this);
                    }
                });
            }
        }
    </script>
</body>

* if문의 조건식에 있는 값을 boolean형으로 형변환할때, 대부분의 값들은 true도 변환됨

false로 변환되는 값 : false, null, NaN, undefined, 0, 빈문자열("")