[목차]
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 문자 키를 눌렀을 때 연속적으로 발생
포커스
이벤트 타입 이벤트 발생 시점 focus HTML 요소가 포커스를 받았을 때 (버블링 x) blur HTML 요소가 포커스를 잃었을 때 (버블링 x) focusin HTML 요소가 포커스를 받았을 때 (버블링 o) focusout HTML 요소가 포커스를 잃었을 때 (버블링 o)
폼
이벤트 타입 이벤트 발생 시점 submit form 요소 내의 submit 버튼을 클릭했을 때 reset form 요소 내의 reset 버튼을 클릭했을 때
값 변경
이벤트 타입 이벤트 발생 시점 input input(text, checkbox, radio), select, textarea 요소의 값이 입력되었을 때 change input(text, checkbox, radio), select, textarea 요소의 값이 변경되었을 때
리소스 및 UI
이벤트 타입 이벤트 발생 시점 load DOMContentLoaded 이벤트가 발생한 이후, 모든 리소스(이미지, 폰트 등)의 로딩이 완료되었을 때 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. 이벤트 객체
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