멋쟁이v의 개발일지

[Javascript] DOM객체 이벤트 본문

0년차/Javascript

[Javascript] DOM객체 이벤트

멋쟁이v 2023. 5. 20. 21:17
728x90
320x100

[목차]


01. 이벤트

  • 이벤트란?
    💡
    웹페이지에서 마우스 클릭, 키보드를 입력, 특정요소에 포커스가 이동될 때 어떤 사건을 발생

    이벤트가 발생했을 때 호출될 함수를 브라우저에게 알려 호출을 위임

    ➡️ 이벤트 핸들러 : 이벤트가 발생했을 때 호출될 함수

    ➡️ 이벤트 핸들러 등록 : 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것

02. 이벤트 루프와 동시성

  • 관련 용어

    ➡️ Call Stack(호출 스택)

    💡
    함수가 호출되면 호출된 함수는 순차적으로 Call Stack에 쌓이게 되고 순차적으로 실행.

    단 하나의 Call Stack을 사용하기 때문에 해당 함수가 종료하기 전까지는 다른 어떤 함수도 실행될 수 없음.

    ➡️ Heap

    💡
    동적으로 생성된 객체 인스턴스가 활당되는 영역

    ➡️ Event Queue(Task Queue)

    💡
    비동기 처리 함수의 콜백 함수, 비동기식 이벤트 핸들러, Timer 함수(setTimeout(), setInterval())의 콜백 함수가 보관되는 영역으로 이벤트 루프(Event Loop)에 의해 특정 시점(Call Stack이 비어졌을 때)에 순차적으로 Call Stack으로 이동되어 실행.

    ➡️ Event Loop(이벤트 루프)

    💡
    Call Stack 내에서 현재 실행중인 함수가 있는지 그리고 Event Queue에 함수가 있는지 반복하여 확인한다.

    만약 Call Stack이 비어있다면 Event Queue 내의 함수가 Call Stack으로 이동하고 실행된다.

03. 이벤트 타입

  • 마우스
    이벤트 타입이벤트 발생 시점
    click마우스 버튼을 클릭했을 때
    dbclick마우스 버튼을 더블 클릭했을 때
    mousedown마우스 버튼을 누르고 있을 때
    mouseup누르고 있던 마우스 버튼을 뗄 때
    mousemove마우스를 움직일 때 (터치스크린에서 동작 x)
    mouseover마우스를 요소 위로 움직였을 때 (터치스크린에서 동작 x)
    mouseout마우스를 요소 밖으로 움직였을 때 (터치스크린에서 동작 x)

  • 키보드
    이벤트 타입이벤트 발생 시점
    keydown모든 키를 눌렀을 때 발생
    keyup누르고 있던 키를 놓았을 때 한번만 발생
    keypress문자 키를 눌렀을 때 연속적으로 발생

  • 포커스
    이벤트 타입이벤트 발생 시점
    focusHTML 요소가 포커스를 받았을 때 (버블링 x)
    blurHTML 요소가 포커스를 잃었을 때 (버블링 x)
    focusinHTML 요소가 포커스를 받았을 때 (버블링 o)
    focusoutHTML 요소가 포커스를 잃었을 때 (버블링 o)

  • 이벤트 타입이벤트 발생 시점
    submitform 요소 내의 submit 버튼을 클릭했을 때
    resetform 요소 내의 reset 버튼을 클릭했을 때

  • 값 변경
    이벤트 타입이벤트 발생 시점
    inputinput(text, checkbox, radio), select, textarea 요소의 값이 입력되었을 때
    changeinput(text, checkbox, radio), select, textarea 요소의 값이 변경되었을 때

  • 리소스 및 UI
    이벤트 타입이벤트 발생 시점
    loadDOMContentLoaded 이벤트가 발생한 이후, 모든 리소스(이미지, 폰트 등)의 로딩이 완료되었을 때
    unload리소스가 언로드 될 때 (주로 새로운 웹페이지를 요청한 경우)
    abort리소스 로딩이 중단되었을 때
    error리소스 로딩이 실패했을 때
    resize브라우저 윈도우(window)의 크기를 리사이즈할 때 연속적으로 발생(window객체만)
    scroll웹페이지(document) 또는 HTML 요소를 스크롤할 때 연속적으로 발생

04. 이벤트 핸들러 등록 및 제거

  • 이벤트 핸들러 등록
    💡
    이벤트 핸들러 어트리뷰트 방식

    ➡️ HTML 요소의 이벤트 핸들러 어트리뷰트에 이벤트 핸들러를 등록하는 방법

    ➡️ <button type=”button” id=”btn” onclick=”alert(’hello’);”>버튼</button>

    이벤트 핸들러 프로퍼티 방식

    ➡️ DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있어 프로퍼티에 직접 함수를 할당하는 방식

    ➡️ 취득요소.onclick = function() { alert(’hello’); }

    addEventListener 메소드 방식

    ➡️ addEventListener 메소드를 이용하여 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 등록

    ➡️ 취득요소.addEventListener(’이벤트타입’, 함수이름, [capture사용 여부]);

  • 이벤트 핸들러 제거
    💡
    removeEventListener 메소드 방식

    ➡️ addEventListener 메소드를 이용하여 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 등록

05. 이벤트 객체

  • 이벤트 객체
    💡
    이벤트가 발생하면 이벤트에 관련한 다양한 정보를 담고 있는 이벤트 객체가 동적으로 생성

    생성된 객체는 이벤트 핸들러의 첫 번째 인수로 전달

    ➡️ const testFn = event ⇒ console.log(event);

    취득요소.addEventListener(’click’, testFn);

06. 이벤트 전파

  • 종류
    💡
    버블링(bubbling), 캡처링(capturing)

    ➡️ 버블링 : 이벤트가 하위요소에서 상위요소 방향으로 전파

    ➡️ 캡처링 : 이벤트가 상위요소에서 하위요소 방향으로 전파

07. DOM 요소의 기본 동작 조작

  • event.preventDefault()
    💡
    DOM 요소의 기본 동작 중단

    ➡️ a 태그인 경우 href속성의 지정된 값(주소)를 통해 특정 링크로 이동하는 기본동작을 중단한다.

  • event.stopPropagation()
    💡
    이벤트 전파를 중지

08. DOM이벤트 코드 예제, 실습

  • [코드예제] 이벤트
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <link rel="icon" href="data:,">
        <title>DOM객체 이벤트</title>
        <style>
          table{
            width: 500px;
            border: 1px solid black;
            table-layout: auto;
          }
          tbody tr td:first-child{
            text-align: center;
            border: 1px solid black;
            width: 150px;
          }
          tbody tr td:last-child{
            text-align: left;
            border: 1px solid black;
          }
          tbody tr td input {
            width: 300px;
          }
          tbody tr td p {
            color: red;
            margin: 0;
          }
          tfoot tr{
            text-align: center; 
          }
          tfoot tr button {
            width: calc(470px/2);
            margin-left: 3px;
            margin-right: 3px;
          }
          #dynamicTb tr input {
            width: calc(470px/3);
          }
          #dynamicTb tr button {
            width: calc(470px/3);
          }
          
        </style>
    </head>
    <body>
        <h1>이벤트</h1>
        - 브라우저는 처리해야 할 특정사건이 발생하면 이를 감지하여 이벤트를 발생<br>
        - 클릭, 키보드입력, 마우스 이동 등이 일어나면 특정한 타입의 이벤트 발생<br>
        - 이벤트 핸들러: 특정한 이벤트가 발생했을 호출될 함수<br>
        - 이벤트 핸들러 등록: 특정이벤트가 발생 시 이벤트 핸들러의 호출을 위임하는 것<br>
    
        <h2>자바스크립트의 이벤트 등록 방식</h2>
        1) html 요소의 속성을 통해 이벤트 등록하는 방식(이벤트핸들러 어트리뷰트 방식) <br>
        2) css 선택자로 객체를 선택하여 이벤트를 등록하는 방식(이벤트 핸들러 프로퍼티 방식) <br>
        3) css 선택자로 객체를 선택하여 addEventListener 메소드로 이벤트를 등록하는 방식 <br>
        
        <h2>html 요소 이벤트 (on 접두사)</h2>
        - 모든 테그는 on이라는 접두사를 가진 속성을 가지고 있다. <br>
        - on 접두사 속성은 이벤트에 관련된 속성이다. <br>
        - on 접두사 속성은 자바스크립트 코드가 문자열로 등록이 가능하다.  <br>
    
        <h1>이벤트의 종류</h1>
        <h2>마우스 이벤트</h2>
        - click : 마우스 버튼을 클릭했을 때<br>
        - dbclick : 마우스 버튼을 더블 클릭했을 때<br>
        - mousedown : 마우스 버튼을 누르고 있을 때<br>
        - mouseup : 누르고 있던 마우스 버튼을 뗄 때<br>
        - mousemove : 마우스를 움직일 때 (터치스크린에서 동작하지 않는다)<br>
        <button type="button" onclick="alert('ksmart47');">어트리뷰트방식1</button>
        <button type="button" onclick="alert47(this);">어트리뷰트방식2</button>
        <button type="button" id="proBtn">프로퍼티방식</button>
        <button type="button" id="methodBtn">핸들러메소드방식</button>
        <button type="button" id="methodBtn2">핸들러메소드방식2</button>
    
        <!-- 실습 1 클릭 횟수 3회 이상이면 클릭해도 이벤트가 발생되지 않도록  -->
        <!--        이벤트 핸들러 메소드를 구현하시오  -->
        <!-- 클릭시 alert() => 클릭횟수 : 1 -->
        <button type="button" id="exBtn">예제</button>
        <script>
          // 예제
          const $exBtn = document.getElementById('exBtn');
          // 클릭 횟수 closure
          const countFn = (cnt => () => ++cnt)(0); // 즉시 실행 함수
          const countFn1 = (cnt => () => ++cnt)(0);
    
          // button 요소에 이벤트 핸들러 등록
          $exBtn.addEventListener('click', function () {
            let count = countFn();
            alert(`클릭횟수 : ${count}`);
            if(count == 3) {
              $exBtn.removeEventListener('click', arguments.callee);
            }
          });
    
          $exBtn.addEventListener('click', exEvent);
          function exEvent() {
            let count = countFn1();
            alert(`클릭횟수 : ${count}`);
            if(count == 3) {
              $exBtn.removeEventListener('click', exEvent);
            }
          }
    
          // 이벤트 핸들러
          function alert47(ele) {
            //alert(ele);
            console.log({ele});
            alert(ele.textContent);
            //alert('한국스마트정보교육원 47기');
          }
    
          // 프로퍼티 방식
          const $proBtn = document.getElementById('proBtn');
          $proBtn.onclick = function () {
            alert('이벤트핸들러 등록 프로퍼티방식 function');
          }
          $proBtn.onclick = () => {
            alert('이벤트핸들러 등록 프로퍼티방식 arrowFn');
          }
    
          // function 키워드로 선언한 함수는 호출시 동적으로 바인딩
          $proBtn.onclick = function () {
            alert(this);
          }
          // 화살표함수로 선언한 함수는 호출시 선언한 위치에서 객체 바인딩
          $proBtn.onclick = () => {
            alert(this);
          }
    
          // 이벤트 핸들러 제거 프로퍼티 방식
          $proBtn.onclick = null;
    
          const $methodBtn = document.getElementById('methodBtn');
          // 이벤트타깃(Dom객체 특정요소).addEventListener('이벤트 타입', 함수 선언문 혹은 함수명)
          $methodBtn.addEventListener('click', function (){
            //alert('이벤트핸들러 등록 addEventListener메소드를 통해 등록');
            alert(this);
    
            // 익명함수의 경우 특정 이벤트 핸들러 안에서 호출
            // 최초 1회 함수 실행 후 익명함수(이벤트핸들러) 제거
            $methodBtn.removeEventListener('click', arguments.callee);
          });
    
          const alertFn = () => {
            //alert('이벤트 핸들러 다중등록 가능');
            alert(this);
          }
          $methodBtn.addEventListener('click', alertFn);
    
          // removeEventListener를 통해 등록한 이벤트핸들러 제거
          $methodBtn.removeEventListener('click', alertFn);
    
          const $methodBtn2 = document.getElementById('methodBtn2');
          $methodBtn2.addEventListener('click', function (){
            console.log(event.isTrusted, '사용자 발생여부 확인');
          });
          $methodBtn2.click();
    
        </script>
    
        <h2>event</h2>
        -preventDefault() : 태그가 가진 기본동작(이벤트)를 중단하는 메소드<br>
    
        <a href="https://www.naver.com" id="naverA">네이버</a>
    
        <script type="text/javascript">
          const $naverA = document.getElementById('naverA');
          $naverA.addEventListener('click', function (e) {
            e.preventDefault();
          });
    
        </script>
    
        <h2>자주 쓰이는 이벤트</h2>
        <!-- 키보드 -->
        - keydown: 모든 키를 눌렀을 때 발생<br>
        - keyup : 누르고 있는 키를 뗄 때<br>
        - keypress : 키를 누르고 뗏을 때<br>
        <!-- 포커스 -->
        - focus : 요소가 포커스를 얻었을 때<br>
        - blur : 요소가 포커스를 잃었을 때<br>
        <!-- form 요소 이벤트 -->
        - input : input 또는 textarea 요소의 값이 변경되었을 때<br>
        - change : select box, checkbox, radio button의 상태가 변경되었을 때<br>
        - submit : form을 전송할 때 <br>
        - reset : reset 버튼을 클릭할 때<br>
    
        <form id="myForm" action="#" method="get">
          <table>
            <tbody>
              <tr>
                <td>
                  <label for="memberId">회원아이디</label>
                </td>
                <td>
                  <input type="text" id="memberId" name="memberId" placeholder="회원아이디를 입력해주세요"/>
                  <p></p>
                </td>
              </tr>
              <tr>
                <td>
                  <label for="memberPw">회원비밀번호</label>
                </td>
                <td>
                  <input type="text" id="memberPw" name="memberPw" placeholder="회원비밀번호를 입력해주세요"/>
                  <p></p>
                </td>
              </tr>
              <tr>
                <td>
                  <label for="checkPw">회원비밀번호확인</label>
                </td>
                <td>
                  <!-- <input type="text"
                         id="checkPw"
                         placeholder="다시 한 번 비밀번호를 입력해주세요"
                         onkeydown="check()"
                         onkeypress="check()"
                         onkeyup="check()"
                         oninput="check()"/> -->
                  <input type="text" id="checkPw" placeholder="다시 한 번 비밀번호를 입력해주세요"/>
                  <p></p>
                </td>
              </tr>
              <tr>
                <td>
                  <label for="memberName">회원이름</label>
                </td>
                <td>
                  <input type="text" id="memberName" name="memberName" placeholder="회원이름을 입력해주세요"/>
                  <p></p>
                </td>
              </tr>
            </tbody>
            <tfoot>
              <tr>
                <td colspan="2">
                  <button type="button" id="confirmBtn" class="btn">확인</button>
                  <button type="reset" id="resetBtn" class="btn">취소</button>
                </td>
              </tr>
            </tfoot>
          </table>
        </form>
        
        <script>
          // keydown -> keypress -> input -> keyup
          function check(){
            console.log({event}, event.type);
          }
    
          const $confirmBtn = document.getElementById('confirmBtn');
          $confirmBtn.addEventListener('click', function () {
            const $myForm = document.querySelector('#myForm');
            const $inputEles = $myForm.querySelectorAll('input');
            console.log($inputEles);
            let isSubmit = true; // submit (유효성 검사 완료 후)
            [...$inputEles].some((item, idx) => {
              let value = item.value; // attribute로 접근 안하고 직접 접근(동적)
              console.log(value);
              // 유효성 검사 방법
              const $pEle = item.nextElementSibling;
              if(typeof value == 'undefined' || value == null || value == '') {
                const labelEle = document.querySelector(`label[for="${item.id}"]`);
                //alert(`${labelEle.textContent}는 필수 입력항목입니다.`);
                $pEle.textContent = `${labelEle.textContent}는 필수 입력항목입니다.`;
                item.focus();
                isSubmit = false;
                return true;
              }
              if(item.id == 'checkPw'){
                const $memberPw = document.getElementById('memberPw');
                if($memberPw.value != value){
                  $pEle.textContent = `입력하신 비밀번호가 일치하지 않습니다.`;
                  value = '';
                  item.focus();
                  isSubmit = false;
                  return true;
                }
              }
              $pEle.textContent = '';
            });
    
            // 유효성 검사 완료시 서버로 폼데이터 전송
            if(isSubmit) $myForm.submit();
          });
    
          // blur : 해당 요소의 포커스 이동될 때 이벤트 타입
          const $inputEles = document.querySelectorAll('#myForm input');
          [...$inputEles].forEach(item => {
            item.addEventListener('blur', function () {
              let value = this.value;
              const $pEle = item.nextElementSibling;
              if(typeof value == 'undefined' || value == null || value == '') {
                const lable = document.querySelector(`label[for="${item.id}"]`).textContent;
                $pEle.textContent = `${lable} 필수 입력항목입니다.`;
                item.focus();
                return false;
              }
              $pEle.textContent = '';
            });
          });
    
          const initCheckPw = () => {
            const $checkPw = document.getElementById('checkPw');
            $checkPw.value = '';
          }
          // change : 해당 요소의 값이 변경될 때 이벤트 타입
          const $memberPw = document.getElementById('memberPw');
          $memberPw.addEventListener('change', initCheckPw);
    
          // input : 해당 요소의 값이 변경될 때 이벤트 타입
          $memberPw.addEventListener('input', initCheckPw);
    
          const $resetBtn = document.getElementById('resetBtn');
          $resetBtn.addEventListener('click', function() {
            const $form = document.querySelector('#myForm');
            const $pEles = document.querySelectorAll('#myForm p');
            $pEles.forEach(item => item.textContent = '');
            $form.reset();
          });
    
        </script>
    
        <h2>이벤트 객체</h2>
        - 이벤트가 발생하면 이벤트에 관련한 다양한 정보를 담고 있는
          이벤트 객체가 동적으로 생성<br>
        - 생성된 객체는 이벤트 핸들러의 첫 번째 인수로 전달
    
        <div>
          <h3>학원</h3>
          <button type="button" id="btn">버튼</button>
        </div>
    
        <script>
    
          const $btn = document.getElementById('btn');
          // 이벤트 핸들러 : 첫번째 매개변수는 이벤트 객체 바인딩
          const eventObj = e => console.log({e});
          $btn.addEventListener('click', eventObj);
    
        </script>
    
        <style>
          #divP{
            width: 500px;
            height: 150px;
            position: relative;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color:aqua;
          }
          #divG{
            width: 450px;
            height: 120px;
            display: grid;
            grid-template-columns: 90px 90px 90px 90px;
            justify-content: center;
            align-items: center;
            text-align: center;
            background-color: darkgoldenrod;
          }
          #divG div{
            height: 80px;
            display: flex;
            align-items: center;
            justify-content: center;
          }
          #divG div a{
            text-decoration: none;
          }
          #divG div{
            background-color: white;
          }
          #divG div a:link{
            color: black;
          }
          #divG div.red{
            background-color: red;
          }
          #divG div.red a:link{
            color: white;
          }
          #divG div.green{
            background-color: green;
          }
          #divG div.green a:link{
            color: white;
          }
          #divG div.yellow{
            background-color: yellow;
          }
          #divG div.yellow a:link{
            color: blue;
          }
          #divG div.blueviolet{
            background-color: blueviolet;
          }
          #divG div.blueviolet a:link{
            color: white;
          }
        </style>
    
        <h2>이벤트 버블링</h2>
        - 이벤트가 하위요소에서 상위요소로 전파되는 것<br>
        - event.prenventDefault() 메소드: DOM 요소의 기본동작 방지하기<br>
                                       ex: a 태그 클릭시 href 주소이동방지<br>
        - event.stopPropagation() 메소드: 이벤트 전파 방지하기<br>
    
        <div id="divP">
          <div id="divG">
            <div class="red"><a href="javascript:void(0);">red</a></div>
            <div><a href="javascript:void(0);">green</a></div>
            <div><a href="javascript:void(0);">yellow</a></div>
            <div><a href="javascript:void(0);">blueviolet</a></div>
          </div>
        </div>
        
        <script>
          const $divP = document.getElementById('divP');
          const $divG = document.getElementById('divG');
          const $divEles = document.querySelectorAll('#divG div');
          const $aEles = document.querySelectorAll('#divG a');
          const $aTag = document.querySelector('#aTag');
    
          const eventStatus = e => {
            // 태그의 기본동작 방지
            e.preventDefault();
            // 이벤트 전파 방지
            //e.stopPropagation();
            const target = e.target.id ? `#${e.target.id}` : `${e.target.tagName}`;
            const currentTarget = e.currentTarget.id ? `#${e.currentTarget.id}` : `${e.currentTarget.tagName}`;
            console.log(`${target} || ${currentTarget}`);
          }
    
          const colorChange = e => {
            // 태그의 기본동작 방지
            e.preventDefault();
    
            const target = e.target;
            const currentTarget = e.currentTarget;
    
            if(target == currentTarget) return false;
    
            const $divEles = document.querySelectorAll('#divG div');
    
            $divEles.forEach(item => {
              switch (target.tagName) {
                case 'A':
                  item.classList.toggle(
                      item.firstElementChild.textContent,
                      item.firstElementChild == target
                  );
                  break;
                case 'DIV':
                  item.classList.toggle(
                    item.firstElementChild.textContent,
                    item.firstElementChild == target.firstElementChild
                  );
                  break;
              }
            });
          }
    
          // 이벤트타겟.addEventListener(이벤트타입, 이벤트핸들러, 캡처링여부);
    
          $aEles.forEach(item => {
            item.addEventListener('click', eventStatus);
            item.removeEventListener('click', eventStatus);
          });
    
          $divEles.forEach(item => {
            item.addEventListener('click', eventStatus);
            item.removeEventListener('click', eventStatus);
          });
    
          $divG.addEventListener('click', eventStatus);
          $divG.removeEventListener('click', eventStatus);
    
          $divG.addEventListener('click', colorChange);
    
          $divP.addEventListener('click', eventStatus);
          $divP.removeEventListener('click', eventStatus);
    
        </script>
    
        <h2>동적바인딩</h2>
        - 이벤트에 의하여 생성된 요소에 이벤트 부여 (이벤트 버블링 활용)
        <table>
          <tbody id="dynamicTb">
            <tr>
              <td>
                <input type="text" name="contents" />
                <button type="button" class="copy-btn">카피</button>
                <button type="button" class="del-btn">제거</button>
              </td>
            </tr>
          </tbody>
        </table>
        <script>
    
          const $tbody = document.querySelector('#dynamicTb');
          const $copyBtns = document.getElementsByClassName('copy-btn');
          const copyFn = e => {
            const element = e.target;
            if(!element.classList.contains('copy-btn')) return false;
    
            const copyEle = element.closest('tr').cloneNode(true);
            copyEle.querySelector('input').value = '';
            $tbody.append(copyEle);
          }
    
          const deleteFn = e => {
            const element = e.target;
            if(!element.classList.contains('del-btn')) return false;
    
            const targetRow = element.closest('tr');
            let length = element.closest('tbody').children.length;
            if(length > 1) targetRow.remove();
          }
    
          /* 카피 했을 때 카피 된 버튼에 이벤트가 실행이 안된다.
          [...$copyBtns].forEach(item => {
            item.addEventListener('click', copyFn);
          });
          */
    
          $tbody.addEventListener('click', copyFn);
          $tbody.addEventListener('click', deleteFn);
    
        </script>
        <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
    </body>
    </html>

  • [코드예제] 이벤트 실습
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <link rel="icon" href="data:,">
        <title>DOM객체 이벤트까지 실습</title>
    </head>
    <body>
        <h2>실습1</h2>
    	실습. 버튼 클릭시 n1요소와 n2요소의 값을 곱하여 result 요소에 결괏값을 
        출력하도록하시오.
    	<br>	
    	<form name="myNum">
    		<input type="text" name="n1"> x
    		<input type="text" name="n2"> =
    		<input type="text" name="result" readonly>
    		<button type="button">계산하기</button>
    	</form>
    	
    	<script type="text/javascript">
    
        const $myNumBtn = document.querySelector('form[name="myNum"] button');
        $myNumBtn.addEventListener('click', function (){
          const $inputEles = document.querySelectorAll('form[name="myNum"] input:not([name="result"])');
          const $inputResult = document.querySelector('form[name="myNum"] input[name="result"]');
          let result = 1;
          let validationCheck = true;
          [...$inputEles].some(item => { // forEach는 요소 수 만큼 반복을 끝까지 다한다. some은 중간에 멈추게 할 수 있다. true를 만나면
            let value = item.value;
            const regex = /^[+-]?\d+(\.?\d*)$/; // 정규표현식
            if(!regex.test(value)){
              alert('숫자를 입력해주세요');
              item.focus();
              validationCheck = false;
              return true;
            }
            /*
            if(typeof value == "undefined" || value == null || value == '') {
              alert('숫자를 입력해주세요');
              item.focus();
              validationCheck = false;
              return true;
            }
            */
            result *= Number(value);
          });
          if(validationCheck) $inputResult.value = result;
        });
    
    	</script>
    
        <h2>실습2</h2>
        실습. section 영역에 구구단 2단이 표형태로 출력되도록 하시오.
        -----------------<br>
        |      2단       |<br>
        -----------------<br>
        |   2 X 1 = 2   |<br>
        -----------------<br>
        |   2 X 2 = 4   |<br>
        -----------------<br>      
        <style>
            table {
                border: 1px solid black;
                width: 200px;
                table-layout: fixed; /* 테이블레이아웃 셀 가로 균등 배분 */
                text-align: center;
            }
            th, td {
                border: 1px solid black;
            }
        </style>
        <section>
            
        </section><br><br>
        <input type="text" id="times" value="" placeholder="단을 입력하시오"/> 
        <button id="guguBtn">구구단 출력</button>
    
        <script type="text/javascript">
    
          const $guguBtn = document.getElementById('guguBtn');
          $guguBtn.addEventListener('click', function() {
            const $times = document.getElementById('times');
            let value = $times.value;
            const regex = /^[1-9]{1}$/;
            if(!regex.test(value)) {
              alert('1~9 사이의 정수만 입력해주세요.');
              $times.value = '';
              $times.focus();
              return false;
            }
    
            const $section = document.querySelector('section');
            const $table = document.createElement('table');
            const $thead = document.createElement('thead');
            const $tbody = document.createElement('tbody');
            //$section.innerHTML = '';
            // replaceChildren() : 인수에 특정 요소를 전달하면 선택한 요소의 자식 요소를 교체하는 메소드
            //                     인수없이 호출 시 선택한 요소의 하위 자식요소는 제거된다.
            $section.replaceChildren();
    
            for(let i=1; i<10; i+=1) {
              if(i==1) {
                const $theadTr = document.createElement('tr');
                const $th = document.createElement('th');
                $th.textContent = `${value}단`;
                $theadTr.append($th);
                $thead.append($theadTr);
              }
              const $tbodyTr = document.createElement('tr');
              const $td = document.createElement('td');
              const numberFormat = num => num > 9 ? `${num}` : `0${num}`;
              let result = `${value} X ${i} = ${numberFormat(value*i)}`;
              $td.textContent = result;
              $tbodyTr.append($td);
              $tbody.append($tbodyTr);
            }
    
            $table.append($thead);
            $table.append($tbody);
            $section.append($table);
          });
    	
    	  </script>
    
        <h2>실습3</h2>
        실습. 아래의 버튼 클릭시 아래의 div 배경 색상이 빨강 파랑 초록으로 순환되도록하시오.
        <br>
        <button type="button" id="bgChangeBtn">배경색전환</button>	
        <div id="bgDiv" style="height: 100px; width: 100px;"></div>
    
        <script type="text/javascript">
          const colorArr = ['red', 'blue', 'green', 'aqua', 'blueviolet', 'deeppink'];
          const cntFn = (cnt => () => cnt++)(0);
    
          const $bgChangeBtn = document.getElementById('bgChangeBtn');
          $bgChangeBtn.addEventListener('click', function () {
            const $bgDiv = document.getElementById('bgDiv');
            let length = colorArr.length;
            $bgDiv.style.backgroundColor = colorArr[cntFn()%length]
          });
    
        </script>
    
        <h2>실습4</h2>
        실습. #allCheck 클릭시 체크가 되었다면 .mCheck 요소 전체는 체크되도록
        체크가 안되어있다면 .mCheck 요소 전체가 체크가 해제되도록 하시오.
        <style>
          #addCateBtn {
            width:100%;
            background-color: #3b5fbf;
            color: white;
            font-weight: bolder;
          }
    			.popup-wrap{
    			    background-color:rgba(0,0,0,.3); 
    			    /* 배경색과 투명도로 살짝 어둡지만 투명한 배경 */
    			    justify-content:center; /*수평 중앙정렬*/
    			    align-items:center;     /*수직 중앙정렬*/
    			    position:fixed;         /* 포지션 픽스, 화면이 스크롤되더라도 고정되기 위함*/
    			    top:0;
    			    left:0;
    			    width: 100%;
    			    height: 100%;               
    			    /* 비활성: display:none */
    			    /* 활성 display:flex; */
    			    display:none; 
    			}
    			.popup{
    			    width:100%;               /*반응형 : 가로값은 100%*/
    			    max-width:400px;          /*팝업의 최대 크기지정*/
    			    border-radius:10px;       /*모달 border 라운드 처리*/
    			    overflow:hidden;          /*각을 없앴을 때 내부 영역이 튀어나오는걸 방지*/
    			    background-color:#264db5; /*배경색*/
    			    /*팝업이 허공에 떠있는 듯한 느낌을 주기 위한 그림자 효과.*/
    			    box-shadow: 5px 10px 10px 1px rgba(0,0,0,.3); 
    			}
    			.popup-body{                /*몸통*/
    			    width:100%;
    			    background-color:#ffffff; /*컨텐츠 영역의 배경색*/
    			}
    			.body-content{              /*몸통 내부 컨텐츠 영역*/
    			    width:100%;
    			    padding-top:10px;             /*좌우에 내용이 붙으면 보기 안좋기 때문에 간격 띄움*/
    			}
    			.body-titlebox{             /*컨텐츠 타이틀 영역*/
    			    text-align:center;        /*제목 중앙정렬*/
    			    width:100%;
    			    height:40px;
    			    margin-bottom:30px;       /*내용과 간격 조정*/
    			}
    			.body-contentbox{           /*컨텐츠 내용 영역*/
    			    word-break:break-word;    /*단어가 짤리지 않음*/
    			    text-align: center;
    			    overflow-y:auto;          /*내부요소가 지정한 세로 값보다 클 경우 스크롤 생성*/
    			    min-height:60px;         /*최소 높이*/
    			    max-height:60px;         /*최대 높이*/
    			}
    			.body-contentbox input{           /*컨텐츠 내용 영역*/
    			    height: 30px;
    			    width: 80%;
    			    border-radius:10px;
    			    border-color: darkviolet;
    			    font-size: 20px;
    			    text-align: center;
    			}
    			.popup-foot{
    			    width:100%;
    			    height:50px;
    			}
    			.pop-btn{ 
    			    display:inline-flex;            /*한줄로 나열하기 위한 inline속성과 flex속성 혼합*/
    			    width:50%;                      /*2개 버튼 각각 50% 영역*/
    			    height:100%;                    /*50px*/
    			    justify-content:center;         /*수평정렬*/
    			    align-items:center;             /*수직정렬*/
    			    float:left;                     /*좌측배치*/
    			    color:#ffffff;                /*글자색*/
    			    cursor:pointer;                 /*마우스 포인터 효과*/
    			}
    			.pop-btn.confirm{                 /*확인버튼*/
    			    border:1px solid #3b5fbf; /*오른쪽 줄*/
    			    background-color: #3b5fbf;
    			}
    			.pop-btn.close{
    			    border:1px solid #bf3b3b; /*오른쪽 줄*/
    			    background-color: #bf3b3b;
    			}
        </style>
        <table>
            <thead>
                <tr>
                  <td>
                    <button type="button" id="addCateBtn">
                      항목추가
                    </button>
                  </td>
                </tr>
                <tr>
                    <td><input type="checkbox" id="allCheck">전체체크</td>
                </tr>
            </thead>
            <tbody id="checkTbody">
                <tr>
                    <td><input type="checkbox" class="mCheck">체크1</td>
                </tr>
                <tr>
                    <td><input type="checkbox" class="mCheck">체크2</td>
                </tr>
                <tr>
                    <td><input type="checkbox" class="mCheck">체크3</td>
                </tr>
            </tbody>
        </table>
    
        <div class="container">
          <div class="popup-wrap" id="popup">
            <div class="popup">
              <div class="popup-body">
                <div class="body-content">
                  <div class="body-titlebox">
                    <h1>항목추가</h1>
                  </div>
                  <div class="body-contentbox">
                    <input type="text" class="cateContent"/>
                  </div>
                </div>
              </div>
              <div class="popup-foot">
                <button class="pop-btn confirm" id="confirm">추가</button>
                <button class="pop-btn close" id="close">취소</button>
              </div>
            </div>
          </div>
        </div>
    
        <script type="text/javascript">
    
    		// 전체 체크를 누르면 체크 항목을 다 체크
        const $allCheck = document.getElementById('allCheck');
        $allCheck.addEventListener('click', function () {
            const $mCheck = document.querySelectorAll('.mCheck');
            $mCheck.forEach(item => {
                item.checked = this.checked;
            });
        });
    
        // tbody안에 체크 항목들을 이벤트 타겟으로 가져와서 확인
        const $checkTbody = document.querySelector('#checkTbody');
        $checkTbody.addEventListener('click', function (e) {
            const target = e.target;
            if(!target.classList.contains('mCheck')) return false;
    
            // 체크 항목들을 전부 체크 했을때 전체항목에 체크되는 함수 호출
            isAllChecked();
        });
    
        // 체크 항목들을 전부 체크 했을때 전체항목에 체크되는 함수 선언
        const isAllChecked = () => {
            const $allCheck = document.getElementById('allCheck');
            const $mCheck = document.querySelectorAll('.mCheck');
            const $checkEle = document.querySelectorAll('.mCheck:checked');
    
            // 체크항목과 체크된 항목의 수가 같다면 전체 항목에 체크
            if($mCheck.length == $checkEle.length) {
                $allCheck.checked = true;
            } else {
                $allCheck.checked = false;
            }
        }
    
        /***** 항목 추가 버튼 클릭 시 *****/
        // 항목 추가하는 창 여는 함수
        const $addCateBtn = document.getElementById('addCateBtn');
        const openPopup = () => {
            const $popup = document.getElementById('popup');
            $popup.style.display = 'flex';
        }
        $addCateBtn.addEventListener('click', openPopup);
    
        // 항목 추가하는 창 닫는 함수
        const $close = document.getElementById('close');
        const closePopup = () => {
            const $cateContent = document.querySelector('.cateContent');
            $cateContent.value = '';
            const $popup = document.getElementById('popup');
            $popup.style.display = 'none';
        }
        $close.addEventListener('click', closePopup);
    
        // 유효성 검사 함수
        const isValid = value => {
            let validationCk = true;
            if(typeof value == 'undefined' || value == null || value == '') {
                alert('항목을 작성해주세요.');
                validationCk = false;
            }
            return validationCk;
        }
    
        // 항목을 추가하는 함수
        const $confirm = document.getElementById('confirm');
        const confirm = () => {
            const $cateContent = document.querySelector('.cateContent');
            let value = $cateContent.value;
    
            // 유효성 검사
            let validationCk = isValid(value);
            if(!validationCk) {
                $cateContent.focus();
                return validationCk;
            }
    
            // tbody 마지막 요소를 복사해서 td 텍스트노드에 value 값을 설정
            const $checkTbody = document.getElementById('checkTbody');
            const $cloneEle = $checkTbody.lastElementChild.cloneNode(true);
            // 체크가 안된 상태로 복사
            $cloneEle.querySelector('.mCheck').checked = false;
            $cloneEle.querySelector('td').lastChild.nodeValue = value;
            $checkTbody.appendChild($cloneEle);
    
            // 전체체크
            isAllChecked();
            // 창닫기
            closePopup();
        }
        $confirm.addEventListener('click', confirm);
    
        </script>
    
        <h2>실습5</h2>
        실습. 상품의 가격을 선택된 수량의 갯수만큽 곱하여 상품가격을 출력하도록 하시오.
        <br><br><br>
        <input type="hidden" id="googsPirce" value="10000">
        <input type="hidden" id="totalPrice" value="0">
        상품 기본 가격 : <span style="color: #f00;">10,000</span>원 <br><br>
        <select id="num" style="width: 150px;">
            <option value="0"> :: 선택 :: </option>
            <option value="5"> 5 개 </option>
            <option value="10"> 10 개 </option>
            <option value="15"> 15 개 </option>
            <option value="20"> 20 개 </option>
        </select>	
        <br><br>
        총 합산 가격 : 
        <span id="totalPriceText" style="color: #f00; font-size: 25px">0</span>원
    
        <script type="text/javascript">
    
          const $num = document.getElementById('num');
          $num.addEventListener('change', function () {
            const $total = document.getElementById('totalPriceText');
            const $span = document.querySelector('span[style="color: #f00;"]');
    
            let price = $span.textContent.replace(',', '');
            let amount = this.value;
            let result = Number(price)*Number(amount);
            /* form을 사용했을 경우
            const $form = document.querySelector('form');
            const $input = document.createElement('input');
            $input.setAttribute('name', 'goodsTotal');
            $input.value = result;
            form.append($input);
            form.submit();
            */
            $total.textContent = result.toLocaleString('ko-KR');
          });
    
        </script>
    </body>
    </html>

09. 기타 정리

  • DOM 프로퍼티 vs HTML 어트리뷰트
    💡
    프로퍼티 : 키와 값으로 구성된 객체 내부의 속성을 의미

    ➡️ DOM 프로퍼티는 자바스크립트에서 객체처럼 행동해 어떤 값이든 가질 수 있고, 대소문자를 구분한다.

    ➡️ 동적으로 변한다.

    어트리뷰트 : HTML 요소 안의 속성을 의미

    ➡️ 어트리뷰터 노드에서 관리하며, 사용자의 입력이 있어도 초기 상태는 변경되지 않는다. (getAttribute, setAttribute가 필요)


tag : #javascript #DOM #프로퍼티 #HTML #어트리뷰트 #이벤트 #루프 #핸들러 #이벤트객체 #버블링 #캡처링 #기본동작조작


Uploaded by N2T

728x90
320x100
Comments