혼공JS - CH 7 문서 객체 모델

Ari·2021년 8월 6일
0

혼자공부하는JS

목록 보기
7/10

혼자공부하는 자바스크립트 유투브 동영상 강의와 책을 보며 정리합니다.
#혼공JS #혼공단 #혼공챌린지

Chapter 07 문서 객체 모델
: DOMContentLoaded 이벤트를 사용하여 문서 객체를 조작해보고 다양한 이벤트의 사용 방법을 이해합니다.

7-1 문서 객체 조작하기

39강 - 라이브 서버와 DOMContentLoaded 이벤트

문서 객체 모델 (Document Object Model -> DOM)
문서 객체 : HTML 요소
문서 객체 모델 : 그걸 조작하는 객체들의 집합


결과

A는 출력되지 않고 오류가 뜬다. 그 이유는 저 시점에서는 아직 body 태그가 아직 안만들어 졌기 때문에 body를 조작할 수 없기 때문

DOM 조작 할 수 있는 위치

DOMContentLoaded를 왜 쓰냐?

  • script 태그를 웬만하면 head 안에서 쓰는것이 좋은데, head태그를 읽어들일 시점(HTML은 위에서부터 아래로 차례대로 읽어온다)에는 아직 body 태그가 안만들어 졌기 때문에 조작이 불가 하다.
    문서객체가 모두 만들어 진 다 음에 이 함수를 실행해달라 라고 하는것이
    document.addEventListener('DOMContentLoaded', () => {}) 이다


문서 객체를 조작할 수 있는 위치:
1.head 테그 속 script 태그 속 DOMContentLoaded 내부 속
2.body 태그 내부의 최하단 script 태그 속

40강 - document.querySelector()/querySelectorAll() 메서드

document.querySelector()

태그를 읽어들이는 메서드. 쿼리셀렉터는 해당하는것 하나만 선택한다. () 안의 해당하는 것들이 여러개일 경우 최상위에 있는 것 하나만 선택됨

클래스 선택자

클래스를 두개 이상 사용할 때 .으로 연결해주고 사이에 띄워쓰기를 넣으면 안된다!

후손선택자

태그 안의 태그 를 선택할 때


'태그'
'#아이디'
'.클래스'
'[타입=텍스트]'

결과

querySelectorAll()

querySelector 는 조건에 맞는 것들 중 가장 처음것 하나만 선택한다
여러개의 태그를 잡고싶은 경우에 사용.

41강 - 문서 객체 조작하기

글자 조작하기

보통 textContent 를 많이 사용하고, innerHTML은 보안상의 문제로 많이 사용하지 않는다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const header1 = document.querySelector('#textContent');
      const header2 = document.querySelector('#innerHTML');
    
      // 조작!
      // 값을 추출한다
      console.log(header1.textContent);
      console.log(header2.innerHTML);

      // 값을 넣는다
      header1.textContent = '넣고싶은<br>문자열';
      header2.innerHTML = '넣고싶은<br>문자열2';

    })
    
  </script>
</head>
<body>
    <h1 id = "textContent">textContent 속성 기존 문자열</h1>
    <h1 id = "innerHTML"> innerHTML 속성 기존 문자열</h1>
</body>
</html>

속성 조작하기

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
       // 조작!
       const img = document.querySelector('img');
      // 값을 넣는다
      img.setAttribute('src','')
      // 값을 추출한다
      

    })
    
  </script>
</head>
<body>
  <img src="" alt="">
  
</body>
</html>

스타일 조작


- 을 쓸 수 없으므로 - 대신 중간에 대문자를 활용해서 작성한다.
-을 쓰기 원한다면 아래처럼 []을 사용할 수도 있으나 저렇게 -를 사용하면 자동완성이 안되기 때문에 잘 쓰지는 않는다!

div.style.height = 10 이렇게 단위를 빼주면 정상적으로 동작하지 않는다!

42강 - 문서 객체 생성, 제거, 이동

<script>
    document.addEventListener('DOMContentLoaded', () => {
        //요소를 만든다
       const header = document.createElement('hi')
       header.textContent = 'createElement로 만든 태그'
       header.style.color = 'red'

       const body = document.querySelector('body')
       //요소를 출력 해준다
       body.appendChild(header)   
    })
    
  </script>

요소 제거

 setTimeout(()=> {
        //  body.removeChild(header)
        header.parentNode.removeChild(header)
       }, 2000)

2초 뒤에 제거가 된다.
두가지 방법이 있지만 parentNode 의 방법을 더 많이 쓴다.

문서객체 이동

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const header = document.createElement('h2')
      header.textContent = '안녕하세요'

      const first = document.querySelector('.first')
      const second = document.querySelector('.second')
      first.appendChild(header)

      setTimeout(() => {
        second.appendChild(header)
      }, 2000)
    })
  </script>
</head>

<body>
  <div class= "first">
    <h1>첫 번째 div 태그 내부</h1>
  </div>
    <hr>
  <div class= "second">
    <h1>두 번째 div 태그 내부</h1>
  </div>
</body>
</html>

이동도 별 다를거 없이 append 를 쓰면 된다

Timer 관련 테크닉(1)

  • setTimeout을 재귀적으로 사용 (정확히는 쓰레드풀 큐를 활용)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const header = document.createElement('h2')
      header.textContent = '안녕하세요'

      const first = document.querySelector('.first')
      const second = document.querySelector('.second')
      first.appendChild(header)

      const toFirst = () => {
        first.appendChild(header)
        setTimeout(toSecond, 1000)
      }
    
      const toSecond = () => {
        second.appendChild(header)
        setTimeout(toFirst, 1000)
      }
      
      toFirst()

    })
  </script>
</head>

<body>
  <div class= "first">
    <h1>첫 번째 div 태그 내부</h1>
  </div>
    <hr>
  <div class= "second">
    <h1>두 번째 div 태그 내부</h1>
  </div>
</body>
</html>

Timer 관련 테크닉(2)

  • 배열을 사용해 간단히!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const header = document.createElement('h2')
      header.textContent = '안녕하세요'

      const arr = [
        document.querySelector('.first'),
        document.querySelector('.second'),
      ]
      
      let counter = 0
      console.log(arr[counter % 2]);

      const fun = () => {
        arr[counter % 2].appendChild(header)
        counter ++
      }
      setInterval(fun, 1000)
      fun()
    })
  </script>
</head>

<body>
  <div class= "first">
    <h1>첫 번째 div 태그 내부</h1>
  </div>
    <hr>
  <div class= "second">
    <h1>두 번째 div 태그 내부</h1>
  </div>
</body>
</html>
  • Second 와 second 는 다르다 .. 대소문자를 잘 구분하자 ㅠㅠ

정리

문서객체를 만들때는 document.createElement 를 활용한다
문서객체를 붙이거나 이동할 때는 appendChild
제거할 때는 header.parentNode.removeChild 사용

43강 - 문서 객체 이벤트 기본

<script>
    document.addEventListener('DOMContentLoaded', () => {
      const header = document.createElement('h1')
      document.body.appendChild(header)

      let count = 0
      header.style.userSelect = 'none'
      header.innerText = ` 클릭횟수 : ${count}`
      header.addEventListener('click', () =>{
        count++
        header.innerText = ` 클릭횟수 : ${count}`

      })
    })
  </script>

책과 같이 body 안에 h1 태그를 이미 넣어줘도되고, 저렇게 createElement를 사용해 추가해줘도 된다.
책과 같이 CSs를 직접 수정해도 되고 , header.style.userSelect = 'none' 를 사용해도 된다.
결과

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
    <script>
      const header = document.createElement('h1')
      header.innerText = `클릭 횟수 : 0`
      const p = document.createElement('p')
      p.innerText = `이벤트 연결 상태: 해제`
      const connectBtn = document.createElement('button')
      connectBtn.innerText = `이벤트 연결버튼`
      const disconnectBtn = document.createElement('button')
      disconnectBtn.innerText = `이벤트 해제버튼`

      document.body.appendChild(header)
      document.body.appendChild(connectBtn)
      document.body.appendChild(disconnectBtn)
      document.body.appendChild(p)


    </script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
    <script>
      let counter = 0
      const listener = () => {
        // counter++
        header.textContent = `클릭 횟수 : ${++counter}`
      }
      // console.log(counter);
      
      const header = document.createElement('h1')
      header.textContent = `클릭 횟수 : 0`
      

      const p = document.createElement('p')
      p.textContent = `이벤트 연결 상태: 해제`

      const connectBtn = document.createElement('button')
      connectBtn.textContent = '이벤트 연결버튼'
      connectBtn.addEventListener('click', () => {
        header.addEventListener('click', listener)
        p.textContent = '이벤트 연결 상태: 연결'

      })

      const disconnectBtn = document.createElement('button')
      disconnectBtn.textContent = '이벤트 해제버튼'
      disconnectBtn.addEventListener('click', () => {
        header.removeEventListener('click', listener)
        p.textContent = '이벤트 연결 상태: 해제'

      })

      document.body.appendChild(header)
      document.body.appendChild(connectBtn)
      document.body.appendChild(disconnectBtn)
      document.body.appendChild(p)


    </script>
</body>
</html>


확인문제 2. 요소들을 querySelector() 메소드로 선택 시 사용할 수 있는 선택자들

7-2 이벤트 활용

44강 - 이벤트 객체, 이벤트 발생 객체, 기본 이벤트

keydown, keyup

keydown -> keypress -> 입력양식에 값이 들어감 -> keyup

<script>
      document.addEventListener('DOMContentLoaded', () => {
        document.addEventListener('keydown', () =>{
          document.body.innerHTML = '<h1>keydown</h1>'
        })

        document.addEventListener('keyup', () =>{
          document.body.innerHTML = '<h1>keyup</h1>'
      })
    }) 
      
    </script>

*** textContent 를 사용하면 동작하지 않는다..

 <script>
      document.addEventListener('DOMContentLoaded', () => {
        document.addEventListener('keydown', (event) =>{
          document.body.innerHTML = `<h1>keydown ${event.code}</h1>`
        })

        document.addEventListener('keyup', () =>{
          document.body.innerHTML = '<h1>keyup</h1>'
      })
    }) 
      
    </script>


누른 키를 보여줌.
어떤 이벤트가 발생 했을 시 그 이벤트와 관련된 변수는 콜백함수의 첫번째 매개변수로 가지고 온다. e 로 짓거나 event 로 해주는 편.

p331 입력된 글자 수 세서 나타내기

 <script>
      document.addEventListener('DOMContentLoaded', () => {
        const h1 = document.querySelector('h1')
        const textarea = document.querySelector('textarea')
        textarea.addEventListener('keyup', () => {        
          h1.textContent = `글자 수 : ${textarea.value.length}`         
      })       
    }) 
    </script>

keyup을 쓰기때문에 키를 뗀 후에 글자수가 세어진다.
keypreess를 쓰면 한국어는 카운팅이 안된다.. - 해결법은 나중에 다룬다

이벤트 발생 객체

아래 코드에서 textarea가 이벤트 발생 객체

     textarea.addEventListener('keyup', () => {        
          h1.textContent = `글자 수 : ${textarea.value.length}`         
      })       
textarea.addEventListener('keyup', (event) => {
          h1.textContent = `글자 수 : ${event.currentTarget.value.length}`         
      })    

이렇게 쓰는 것과 같다.

  textarea.addEventListener('keyup', function () {
          h1.textContent = `글자 수 : ${this.value.length}`         
      })

그냥 익명함수 + this 키워드를 사용하는 방법도 있다.

위의 3가지 코드 모두 활용이 많이 된다. event.currentTarget 가 그중에서도 가장 중요!

기본이벤트 막기

<script>
       document.addEventListener('DOMContentLoaded', () => {
       const a = document.querySelector('a')
       //클릭시 링크 이동 막기
       a.addEventListener ('click',(event) => {
        event.preventDefault()
       }) 
       //우클릭막기
       a.addEventListener('contextmenu', (event) => {
         event.preventDefault()
       })

      }) 
    </script>
</head>

<body>
  <a href="http://google.com">링크</a>
</body>

45강 - 입력 양식: 버튼

    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const buttonA = document.querySelector('button')
        const buttonB = document.querySelector('input[type=button]')
        buttonA.addEventListener('click', (event) => {
          event.currentTarget.textContent += '글자'
        })

        buttonB.addEventListener('click', (event) => {
          event.currentTarget.value += '글자2'
        })


     

      }) 
    </script>
</head>

<body>
  <!-- click 이벤트 -->
  <button>글자</button>
  <input type="button" value="글자">

</body>

submit은 form 태그 내부에 위치하여야 한다
button 태그도 input[type=submit] 처럼 동작을 한다

submit 이벤트

form 태그 내에 button 태그를 넣게 되면 클릭시 submit이 되게 된다
그래서 submit 아닌 버튼을 넣고싶을 때는
1. input type= button 으로 넣어줘야한다.
2. 또는 input type= button 으로 넣고 preventDefault() 해줘야한다

46강 - 입력 양식: 글자 입력

<p contenteditable="true">dd</p>

미션

기본 미션 : p.315의 <직접 해보는 손코딩>을 실행한 후 출력되는 고양이 이미지 인증샷

 <script>
    document.addEventListener('DOMContentLoaded', () => {
       // 조작!
       const img = document.querySelector('img');
       // 값을 넣는다
       img.setAttribute('src','http://placekitten.com/200/200')
       
       // 값을 추출한다
       console.log(img.getAttribute('src'));

    })
    
  </script>

이렇게 쓰거나 또는

 <script>
    document.addEventListener('DOMContentLoaded', () => {
       // 조작!
       const img = document.querySelector('img');
       // 값을 넣는다
       img.src = 'http://placekitten.com/200/200';
       // 값을 추출한다
       console.log(img.src);

    })
    
  </script>

이렇게 표준에 있는 속성은 img.src로 바로 활용할 수 있다.
표준에 없는 속성들은 무조건 setAttributegetAttribute를 활용해야 한다


change 이벤트 : 해당 input 태그 전체의 입력을 마쳤을 때 (enter를 쳤을때나 입력양식을 떠났을 때)


let [seconds, timerID] = [0, 1] 이렇게 쓰게 되면 seconds 에는 0, timerID 에는 1이 들어가게 됨.
저렇게 쓰는걸 다중할당연산 이라고 부름

선택 미션 :p.352 누적 예제를 활용하여 본인의 할 일 목록을 만들어 인증샷

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const addTodo = () => {
        if (input.value !== '') {
          const div = document.createElement('div')
          document.body.appendChild(div)

          const checkbox = document.createElement('input')
          checkbox.type = 'checkbox'
          checkbox.addEventListener('change', () => {
            if (checkbox.checked) {
              div.style.textDecoration = 'line-through'
            } else {
              div.style.textDecoration = ''
            }
          })
          div.appendChild(checkbox)

          const span = document.createElement('span')
          span.textContent = input.value
          input.value = ''
          div.appendChild(span)

          const deleteButton = document.createElement('button')
          deleteButton.textContent = '제거하기'
          deleteButton.addEventListener('click', () => {
            div.parentNode.removeChild(div)
          })
          div.appendChild(deleteButton)
        }
      }

      const h1 = document.createElement('h1')
      h1.textContent = '할 일 목록'
      document.body.appendChild(h1)
      
      const input = document.createElement('input')
      input.addEventListener('keyup', (event) => {
        if (event.keyCode === 13) {
          addTodo()
        }
      })
      document.body.appendChild(input)

      const addButton = document.createElement('button')
      addButton.textContent = '추가하기'
      document.body.appendChild(addButton)
      addButton.addEventListener('click', () => {
        if (input.value !== '') {
          addTodo()
        }
      })
    })
  </script>
</head>
<body>

</body>
</html>

profile
junior developer 🐤

0개의 댓글