-
[멋쟁이사자처럼] 프론트엔드 스쿨 7기 - 33일차 기록 및 복습 (DOM target, Class)Front-end 개발 2023. 8. 22. 11:50
목차
1. DOM target
2. class
1. DOM target
1-1. target
- 브라우저 화면에서 이벤트가 발생하면, 브라우저는 이벤트 대상을 찾아가는 과정에서 이벤트가 차례로 실행되는 이벤트 전파(event propagation) 이 발생한다.
- 부모부터 자식까지 일련의 요소를 모두 타고가며 진행되는 이러한 이벤트의 특징 덕분에 이벤트 객체에는 target, currentTarget 이라는 속성이 존재한다.
- target 속성에는 이벤트가 발생한 진원지의 정보가 담겨 있다.
- target 속성을 통해 이벤트 리스너가 없는 요소의 이벤트가 발생했을 때도 해당 요소에 접근 할 수 있다.
- currentTarget 속성에는 이벤트 리스너가 연결된 요소가 참조되어 있다.
<html> <article class="parent"> <ol> <li><button class="btn-first" type="button">버튼1</button></li> <li><button type="button">버튼2</button></li> <li><button type="button">버튼3</button></li> </ol> </article> <script> const parent = document.querySelector('.parent'); parent.addEventListener('click', function (event) { console.log(event.target); // 선택한 요소 <button> console.log(event.currentTarget); // EventListener에 연결된 요소 <article> }) </script> </html>
1-2. 이벤트 위임(Event Delegation = 캡쳐링&버블링, 이벤트 전파) : 면접 질문
- 이벤트 리스너가 없어도 마치 리스너가 있는 것 처럼 사용 할 수 있는 테크닉을 이벤트 위임이라고 한다.
- event.target.nodeName 은 대문자로 해야 한다.
- 캡처링, 버블링 단계는 이벤트가 발생하고 찾기 위해서 일어난다. DOM 트리 최상위부터 훓게 된다.
- button에는 하위요소가 텍스트기 때문에 텍스트만 바뀌지만 li는 하위요소에 button이 있어서 button까지 텍스트로 바뀐다.
<body> <article class="parent"> <ol> <li><button class="btn-first" type="button">버튼1</button></li> <li><button type="button">버튼2</button></li> <li><button type="button">버튼3</button></li> </ol> </article> <script> const parent = document.querySelector('.parent'); parent.addEventListener('click', function (event) { console.log(event.target); if (event.target.nodeName === "BUTTON") { event.target.textContent = "버튼4"; } if (event.target.nodeName === "LI") { event.target.textContent = "버튼4"; } // console.log(event.currentTarget); }) </script> </body>
1-2. 이벤트의 this
- 이벤트 리스너 콜백 함수의 this 값은 이벤트가 연결된 노드를 참조한다.
- 일반 함수일 경우에는 이벤트가 발생하는 노드 객체를 가리킨다.
<body> <article class="parent"> <ol> <li><button class="btn-first" type="button">버튼1</button></li> <li><button type="button">버튼2</button></li> <li><button type="button">버튼3</button></li> </ol> </article> <script> const parent = document.querySelector('.parent'); parent.addEventListener('click', function (event) { console.log(this); console.log(event.currentTarget); // 이벤트가 발생한 대상을 조건식으로 특정하기 if (event.targetnodeName == "LI") { event.target.textContent = "hello"; } }); </script> </body>
- 이벤트 리스너 콜백 함수가 화살표 함수일 경우 this 값은 상위 스코프를 가리킨다.
<body> <article class="parent"> <ol> <li><button class="btn-first" type="button">버튼1</button></li> <li><button type="button">버튼2</button></li> <li><button type="button">버튼3</button></li> </ol> </article> <script> const parent = document.querySelector('.parent'); parent.addEventListener('click', (event) => { console.log(this); console.log(event.currentTarget); // 이벤트가 발생한 대상을 조건식으로 특정하기 if (event.targetnodeName == "LI") { event.target.textContent = "hello"; } }); </script> </body>
- 상위 스코프에서 this 값을 상속받는 화살표 함수를 사용하여 메서드를 정의해볼 수 있다.
- this 는 전역이거나 객체(인스턴스)이거나 상위스코프거나 3종류로 좁혀서 생각할 수 있다.
myObj = { name: 'jaehyun', walk() { parent.addEventListener('click', () => { console.log(this.name, 'is walking'); }); } }; myObj.walk();
1-3. select box 이벤트 위임으로 개선하기
- 추가된 총 기능
(1) 버튼을 누르면 목록이 나와야한다.
(2) 목록의 버튼을 누르면 버튼의 텍스트가 첫번째 버튼에 반영되어야 한다.
(3) li 를 동적으로 생성한다.
(4) 이벤트를 이벤트 위임을 통해서 구현한다.
<body> <h3>셀렉트 박스 만들기</h3> <div class="custom-select"> <button class="btn-select">최애 프로그래밍 언어</button> <ul class="list"> </ul> </div> <script> // 1. 버튼을 누르면 목록이 나와야한다. const btn = document.querySelector('.btn-select'); btn.addEventListener('click', function () { btn.textContent = '최애 프로그래밍 언어'; btn.classList.toggle('on'); }); // 2. 목록의 버튼을 누르면 버튼의 텍스트가 첫번째 버튼에 반영되어야 한다. // const btns = document.querySelectorAll('.list button'); // node list (유사배열객체) // btns.forEach((itemBtn) => { // itemBtn.addEventListener('click', function () { // console.log(itemBtn.textContent); // btn.textContent = itemBtn.textContent; // btn.classList.remove('on'); // }) // }) // 3. li 를 동적으로 생성해주세요. const list = document.querySelector('.list'); const arrLang = ['Python', 'Java', 'JavaScript', 'C#', 'C/C++']; // HTML 요소를 생성하는 예전 방식 // arrLang.forEach((item) => { // const li = document.createElement('li'); // const btn = document.createElement('button'); // btn.setAttribute('type', 'button'); // btn.textContent = item; // // li.appendChild(btn); // appendChild 는 생성된 요소를 반환한다. // // list.append(li); // append는 반환값이 없다. // list.appendChild(li).appendChild(btn); // 연결해서 쓸 수 있다. // }) // 배열을 통한 동적인 요소 생성 arrLang.forEach((item) => { const li = document.createElement('li'); li.innerHTML = `<button type='button'>${item}</button>` list.appendChild(li); // append는 반환값이 없다. }) // 4. 이벤트를 이벤트 위임을 통해서 구현해주세요. list.addEventListener('click', (event) => { // 2번의 반복문 보다 더 빠르다. // console.log(event.target.nodeName); if (event.target.nodeName === "BUTTON") { btn.textContent = event.target.textContent; btn.classList.remove('on'); // 가장 가까운 상위 요소 제거 } }) </script> </body>
1-4. preventDefault()
- 브라우저의 기본 이벤트 동작을 취소한다.
- 브라우저는 HTML 태그를 통해 여러가지 기능들을 제공한다.
- 하지만 때때로 그러한 기능이 방해가 되는 경우가 있다.
- 이렇듯 종종 브라우저의 기본 동작을 중지하고 자바스크립트를 통해 기능을 처리하고자 할 때 사용한다.
- <a> 태그의 링크 이동을 막고, form 의 제출(submit)을 중단하는 예제
<body> <a href="https://www.naver.com" class="link">네이버 링크입니다만..</a> <form action=""> <button type="submit" class="submit">제출</button> </form> <script> const link = document.querySelector('.link'); link.addEventListener('click', (event) => { console.log('clicked'); // event.preventDefault(); }) const myForm = document.querySelector('form'); myForm.addEventListener('submit', () => { console.log('submit!!'); }); </script> </body>
- form 은 서버 사이트(server side) 렌더링을 기준으로 개발되었고, 제출(submit) 이벤트 발생 후 새로 고침된다.
- 서버 사이트 렌더링은 form 을 받고 서버에서 클라이언트에 완성된 HTML 화면 전체를 보낸다.
- react.js, vue.js 프레임워크인 SPA 특징은 새로 고침이 되지 않고, 전달 받은 데이터를 브라우저에서 화면을 만든다.
1-5. preventDefault 실습
- 이벤트 타입 'contextmenu' 를 이용해 브라우저 상의 마우스 오른쪽 버튼 이벤트를 막고,
- '해당 페이지에서는 오른쪽 클릭을 제한한다.' alert 띄우기<script> document.addEventListener('contextmenu', (event) => { event.preventDefault(); alert('해당 페이지에서는 오른쪽 클릭을 제한합니다!!'); }) </script>
1-6. stopPropagation
- 앞에서 우리는 preventDefault를 통해 브라우저의 기본 이벤트 동작을 취소 해봤다.
- 하지만 이때 우리가 앞에서 배웠던 이벤트 흐름, 즉 이벤트 전파(event propagation)를 막지는 못한다.
- 이벤트 전파를 의도적으로 중단하고 싶을 경우에는 stopPropagation 을 사용한다.
- 캡처링/버블링 이벤트와 복합적으로 생각할 수 있는 예제이다.
<body> <form action=""> <button type="submit" class="submit">제출</button> </form> <script> const btn = document.querySelector('form'); btn.addEventListener('click', (event) => { event.preventDefault(); // btn 의 submit 동작은 막는다. console.log('submit'); // 이벤트의 흐름을 차단합니다. event.stopPropagation(); // 이후 이벤트 전파(bubling) 단계를 막는다. }) // default === false, 버블링 이벤트 // 캡처링 이벤트로 만들면 버블링 이벤트에서 전파를 막아도 소용없습니다. document.body.addEventListener('click', () => { console.log('event still alive!!') }, true); // capturing event (btn 보다 body 의 event가 먼저 실행된다) </script> </body>
1-7. stopPropagation 실습
- 내용
(1) p 태그를 클릭하면 p 태그의 컨텐츠를 출력하는 alert 창을 띄워주고(2) 삭제 버튼을 클릭하면 삭제할 것인지를 물어보는 confirm 창을 띄워주고, 확인을 누르면 P태그를 삭제한다.
- window.confirm('삭제하시겠습니까?') 는 확인을 누르면 true 를 리턴하고, 취소를 누르면 false 반환
<body> <h1>나의 todo list</h1> <p>1. 오늘 저녁에는 부대찌게를 끓여 먹겠다.<button type="button">삭제</button></p> <p>2. 후식으로 슈팅스타를 먹겠다.<button type="button">삭제</button></p> <p>3. 자기 전에 반드시 내일 아침 메뉴를 생각해두겠다.<button type="button">삭제</button></p> <script> // 1. P 태그를 클릭하면 p 태그의 컨텐츠를 출력하는 alert 창을 띄워주고 // 2. 삭제 버튼을 클릭하면 삭제할 것인지를 물어보는 confirm 창을 띄워주고, 확인을 누르면 P태그를 삭제합니다. // p 태그를 선택하여 클릭 이벤트를 붙여줍니다. const ps = document.querySelectorAll('p'); ps.forEach((item) => { item.addEventListener('click', (event) => { alert(item.firstChild.textContent); // console.log(item.firstChild.replace('삭제','')); // console.log(item.firstChild.slice(0,-2)); }); // }, true); // 캡처링(true)으로 바꿔주면 alert 가 먼저 실행된다. }); // btn 태그를 선택하여 클릭 이벤트를 붙여줍니다. const btns = document.querySelectorAll('button'); btns.forEach((item) => { item.addEventListener('click', (event) => { const result = confirm('삭제하시겠습니까?'); event.stopPropagation(); // 버블링 이벤트 전파를 중단한다. if (result) { item.closest('p').remove(); // 상위 가장 가까운 'p' // item.parentElement.remove(); // 상위 요소 } }); }); </script> </body>
2. Class
2-1. 객체지향 프로그래밍
- 소프트웨어 개발 방법론은 크게 정보 공학/객체지향/컴포넌트 기반/애자일 방법론으로 나눌 수 있다.
- 객체지향 프로그래밍(Object Oriented Programming, OOP)은 프로그래밍 방법론 중에 하나로,
- 프로그램을 작성할 때 객체들을 만들어 서로 소통하도록 하는 방법이라고 할 수 있다.
- 우리가 앞서서 배운 자바스크립트 객체는 데이터의 묶음이라면 객체 지향의 객체는 우리가 표현하고자 하는 구체적인 사물을 추상적으로 표현한 것이라고 볼 수 있다.
- 그리고 또한 객체는 행동과 상태를 가진다. 여기서 행동은 메소드, 상태는 프로퍼티 정도로 이해하면 된다.
- 객체와 객체가 서로 메소드를 통해 상호작용하게 하는것이 바로 객체지향 프로그래밍이라고 할 수 있다.
<body> <script> const me = { name: '한재현', address: '제주도 제주시 인다 1길', phoneNum: '010-8000-0000', canWalk: function () { console.log('재현이가 걷는다.'); }, teaching: function (student) { student.levelUp(); } } const lion = { level: 1, levelUp: function () { this.level++; } } me.teaching(lion); console.log(lion); </script> </body>
2-2. 생성자 (constructor)
- 생성자란 객체를 만들 때 new 연산자와 함께 사용하는 함수
- 생성자의 장점은 생성자를 통해 생성된 객체는 같은 프로퍼티와 메서드를 공유할 수 있다는 것이다.
- 아래는 새로운 배열을 생성하는 내장 생성자의 예시이다.
let myArr = new Array(1,2,3);
- 자신만의 생성자를 만들 수도 있다. 생성자는 함수이기 때문에 기본적으로 함수가 필요하다.
- 생성자 함수는 암묵적으로 대문자로 시작하는 이름을 가지는 것으로 약속되어 있다.
function Factory(){} let robot1 = new Factory(); console.log(robot1 instanceof Factory); // 생성자를 통해 생성된 instance 인지 확인
- Factory 생성자 함수는 따로 return 값을 가지지 않지만 new키워드가 앞에 붙게되면 실행되었을 때 자동적으로 객체를 생성하고 반환합니다. 이렇게 반환되어 만들어진 객체를 다른 말로 인스턴스(instance) 라고 합니다.
- 생성자 함수와 객체의 관계는 instanceof 로 확인할 수 있습니다.
- 생성자 함수에서는 const 나 let 보다는 this 를 활용한다.
- 생성자 함수의 this 는 인스턴스를 가리킨다.
- 생성자 함수를 사용하면 다시 여러줄로 추상화할 필요없이 객체를 계속 찍어낼 수 있다.
2-3. 생성자 함수 실습
- 실습: 음식 이름의 배열을 전달하면 배열안에서 랜덤하게 메뉴를 뽑아내는 로봇객체의 생성자를 만들어보세요.
<body> <script> // 음식 이름의 배열을 전달하면 배열안에서 랜덤하게 메뉴를 뽑아내는 로봇객체의 생성자를 만들어보세요. function RobotFactory(foodNames) { // Math.random() ===> 배열의 인덱스 this.menu = foodNames; this.sayMenu = function () { // 배열의 길이에 해당하는 범위의 랜덤한 값을 뽑아냅니다. const index = parseInt(Math.random() * this.menu.length); // 0 ~ 2.999 console.log(`삐리비리비리 오늘의 메뉴는! ${this.menu[index]}`); } } const robot = new RobotFactory(['청국장', '물냉면', '돔배고기']); robot.sayMenu(); </script> </body>
2-4. 프로토타입 (prototype)
- 모든 인스턴스들이 공유하는 공간
- 손쉽게 객체를 생산할 수 있지만, 객체의 메서드를 등록 할때마다 새로운 메모리 주소에 함수를 생성하고 있다.
function NewFactory(name){ this.name = name; this.sayYourName = function(){ console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`); } }
- 객체의 메서드를 등록 할 때마다 함수가 선언되고 할당되고 있다.
robot instanceof RobotFactory; // true robot2 instanceof RobotFactory; // true robot.sayMenu === robot2.sayMenu; // false
- 바로 이 부분이 문제입니다. 우리는 100개의 객체를 생성할때마다 역시 100개의 함수를 새로 만들고 있는 것이다.
- 이러한 자원의 낭비를 해결하기 위해 등장한 것이 바로 프로토타입이다.
function NewFactory2(name){ this.name = name; } NewFactory2.prototype.sayYourName = function(){ console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`); } const robot = NewFactory2('john'); const robot2 = NewFactory2('david'); console.log(robot instanceof NewFactory2); // true console.log(robot2 instanceof NewFactory2); // true console.log(robot.NewFactory2 === robot1.NewFactory2); // true
'Front-end 개발' 카테고리의 다른 글
[멋쟁이사자처럼] 2023년 8월 KPT 회고 (0) 2023.08.25 [멋쟁이사자처럼] 프론트엔드 스쿨 7기 - 2차 이력서 특강 (2) 2023.08.25 [멋쟁이사자처럼] 프론트엔드 스쿨 7기 - 32일차 기록 및 복습 (DOM) (0) 2023.08.21 JS 클로저 - 캡슐화와 정보 은닉 (0) 2023.08.21 [이력서] 직무(JD)분석 - 카카오 (지도서비스/FE플랫폼팀) (0) 2023.08.20