본문 바로가기
etc/블록체인 뉴딜일자리사업

자바스크립트 노드 다루기, 이벤트 핸들러

by vellahw 2023. 5. 16.

 

 

💭 서론

교육 10일차

오늘은 오전, 오후 모두 자바스크립트 수업이었다. 노드를 다루면서 이벤트를 연결하고 약간은 머리를 쓰면서 작업을 해서 시간이 순삭 되었다. 기초적인 부분이지만 실제로 프로젝트를 진행하며 비슷하게 구현 해보기도 한 기능들이라 열심히 따라 들었다.
 
 


자바스크립트 기초

 

✔️ 실습 - 버튼 클릭하면 내용 띄우기/닫기

완성본

1. '상세 설명 보기'를 누르면 상세 설명이 표시하기
2. '상세 설명 보기'가 클릭하면 '상세 설명 닫기'로 변경하기
 

<!DOCTYPE html>
<html lang="ko">
<head>
	<title>DOM</title>
	<link rel="stylesheet" href="css/product.css">
</head>
<body>
	<div id="container">
			<h1 id="heading">에디오피아 게뎁</h1>
			<div id="prod-pic">
				<img src="images/coffee-pink.jpg" alt="에디오피아 게뎁" id="cup" width="200" height="200">
					<div id="small-pic">
						<img src="images/coffee-pink.jpg" class="small">
						<img src="images/coffee-blue.jpg" class="small">
						<img src="images/coffee-gray.jpg" class="small">
					</div>
			</div>			
			<div id="desc">
				<ul>
					<li>상품명 : 에디오피아 게뎁</li>
					<li class="bluetext">판매가 : 9,000원</li>
					<li>배송비 : 3,000원<br>(50,000원 이상 구매 시 무료)</li>
					<li>적립금 : 180원(2%)</li>
					<li>로스팅 : 2019.06.17</li>
					<button>장바구니 담기</button>
				</ul>				
				<a href="#" id="view">상세 설명 보기</a>				
			</div>
			<hr>
			<div id="detail">									
					<h2>상품 상세 정보</h2>
					<ul>
						<li>원산지 : 에디오피아</li>
						<li>지 역 : 이르가체프 코체레</li>
						<li>농 장 : 게뎁</li>
						<li>고 도 : 1,950 ~ 2,000 m</li>
						<li>품 종 : 지역 토착종</li>
						<li>가공법 : 워시드</li>
					</ul>
					<h3>Information</h3>
					<p>2차 세계대전 이후 설립된 게뎁 농장은 유기농 인증 농장으로 여성의 고용 창출과 지역사회 발전에 기여하며 3대째 이어져 내려오는 오랜 역사를 가진 농장입니다. 게뎁 농장은 SCAA 인증을 받은 커피 품질관리 실험실을 갖추고 있어 철처한 관리를 통해 스페셜티 커피를 생산합니다.</p>
					<h3>Flavor Note</h3>
					<p>은은하고 다채로운 꽃향, 망고, 다크 체리, 달달함이 입안 가득.</p>
			</div>
	</div>

	<script src="js/product.js"></script>
</body>
</html>

▲ html

let isOpen = false;
const view = document.getElementById('view');
const detail = document.getElementById('detail')
		
view.addEventListener('click', ()=>{
	if(isOpen == false) {
		detail.style.display = 'block';
		view.innerHTML = '상세 설명 닫기';
		isOpen = true;
	} else {
		detail.style.display = 'none';
		view.innerHTML = '상세 설명 보기';
		isOpen = false;
	}
});

▲ js

  • isOpen이란 변수를 만들어 상세설명이 열렸는지에 대한 기본값으로 설정했다. (기본값 false == 닫힘)
  • 닫혔는지 열렸는지에 따른 조건문으로 display 속성과 innerHTML로 텍스트를 변경하여 구현됨

 
 

✔️ DOM 요소 추가하기

노드의 구성

//p 요소 노드 생성 
let newP = document.createElement('p');

//텍스트 노드 생성
let newText = document.createTextNode('벨라 블로그');

//노드와 노드를 연결
newP.appendChild(newText);

//속성 노드 생성
let attr = document.createAttribute('class');
attr.value = 'accent'; //class='accent'

//요소 노드에 속성 노드 연결
newP.setAttributeNode(attr);

//속성 추가 또다른 방법
newP.setAttribute('class', 'accent');

//body에 노드 연결
document.body.appendChild(newP);
  • 속성 노드를 만들 때는 createAttribute()를 사용해 변수에 담은 후 setAttributeNode()로 속성을 추가할 요소 노드에 연결한다.
  • 텍스트 노드를 만들어 웹 문서에 추가해 놓았다면 setAttribute('속성명', '속성 이름')으로 더 간단히 속성을 추가할 수 있다.

 

스크립트로 만든 요소가 body에 추가된 모습
 
 
 

✔️ 실습 - 참가 신청 폼 만들기

완성 모습

  • 신청 버튼을 누르면 입력한 이름 리스트로 띄우기

 

<!DOCTYPE html>
<html lang="ko">
<head>
	<title>DOM - Create & Add Node</title>
	<link rel="stylesheet" href="css/nameList.css">
</head>
<body>
	<div id="container">
		<h1>참가 신청</h1>
		<form action="" onsubmit="return false">
			<input type="text" id="userName" placeholder="이름">
			<button id="applyBtn">신청</button>
		</form>
		<hr>
		<div id="nameList"></div>
	</div>
	<script src="js/register.js"></script>
</body>
</html>

▲ html

const applyBtn = document.getElementById('applyBtn');
const nameList = document.getElementById('nameList');
let userInsertName = document.getElementById('userName')

applyBtn.addEventListener('click', newRegister);

function newRegister() {
    let newP = document.createElement('p');
    let newText = document.createTextNode(userInsertName.value);

    newP.appendChild(newText);

    nameList.appendChild(newP);

    userName.value = '';
}

▲ js

  • 동적으로 노드를 생성하고 input의 value를 얻어와 list에 추가한다.

 

 

✔️ DOM 트리를 이용해 원하는 노드 다루기

- hasChildNodes(): 자식 노드 확인

자식 노드가 있다면 true, 자식 노드가 없다면 false를 반환한다.

'김길동' 노드는 자식노드가 있으므로 true 반환
 

- childNodes

요소 노드뿐만 아니라 태그와 태그 사이의 줄 바꿈도 빈 텍스트 노드인 자식 노드로 인식한다.
 

- children: 요소에만 접근

요소 노드, 텍스트 노드, 주석 노드까지 모두 접근 가능하다.
만약 자식 노드 중에서 텍스트 노드와 주석 노드는 필요하지 않다면 children 속성을 사용하면 됨!
 

- insertBefore(추가할 노드, 추가될 위치): 원하는 위치에 노드 삽입

부모 노드에 자식 노드를 추가할 때 기준이 되는 노드를 지정하고 그 앞에 자식 노드를 추가할 수 있다.
노드의 위치를 바꾸는 효과를 낼 수 있다.

children 속성과 인덱스를 이용해 노드에 접근하고 insertBefore()를 이용해 노드의 위치 변경

 

위 참가 신청 코드는 appendChild()를 이용해 노드의 맨 마지막에 새로 입력한 이름이 추가되도록 작성 했지만 insertBefore()를 이용해 아래와 같이 수정하면 새로 입력한 이름이 최상단에 오도록 할 수 있다.

//nameList.appendChild(newP);

//맨 위에 추가되게 수정
nameList.insertBefore(newP, nameList.childNodes[0]);

 
 
- removeChild(삭제할 자식 노드): 특정 노드 삭제하기

노드 자신을 직접 삭제할 수 없기 때문에 부모 노드에서 자식 노드를 찾는 방식을 이용해야 한다.
 
 🔗 실습 - 삭제 버튼 만들고 해당 이름 삭제하기

완성

    applyBtn.addEventListener('click', register);
   
    function register() {
        
        //p 태그로 이름 추가
        let newP = document.createElement('p');
        let newText = document.createTextNode(userInsertName.value);
        newP.appendChild(newText);
        
        //삭제 버튼 생성
        let newDelBtn = document.createElement('span');
        let delText = document.createTextNode('X');
        newDelBtn.appendChild(delText);
        newDelBtn.setAttribute('class', 'del'); //css 적용을 위한 속성 추가
        
        //이름과 버튼 연결
        newP.appendChild(newDelBtn);
        
        //맨 위에 추가
        nameList.insertBefore(newP, nameList.childNodes[0]);
        userName.value = '';

		//삭제 버튼에 이벤트 연결
        newDelBtn.addEventListener('click', ()=> {
            if(newP.parentNode) { //nameList가 있다면
                newP.parentNode.removeChild(newP);
            }
        });
    }

위 js의 register 함수를 위와 같이 수정했다.
createElement-createTextNode로 X 버튼을 생성하고 이벤트를 걸어줬다.
newP(사용자가 입력한 이름과 X 버튼 노드)의 부모노드가 있다면 노드 삭제를 진행하는데 여기서 newP의 부모 노드는 nameList 노드이다. 즉 리스트가 있어야 해당 노드가 삭제된다.
newP의 부모 노드를 찾아 removeChild()로 자식 노드인 자신(newP)을 삭제한다.
 
💬 수업에선 for문을 이용해서 버튼 하나하나에 반복문을 걸어주는 방식으로 설명해주셨는데 굳이 for문을 이용하지 않아도 될 것 같아 응용해보았다. 이벤트 핸들러 안에 또 이벤트 핸들러가 있는거라 이벤트가 중첩되지 않을까 생각 했는데 클로저로 인해 각각의 핸들러가 자신만의 독립적인 렉시컬 환경을 유지하므로 이벤트가 중첩되지 않는다고 한다!
 

🐯 호랑이 굴에 들어가야 하는 렉시컬 환경 (출처: https://ko.javascript.info/closure)

 
 

✔️ form 폼 제어

1. 체크박스의 checked 속성 이용하기

let checkBox = document.getElementById('shippingInfo');
let billingName = document.getElementById('billingName');
let billingTel = document.getElementById('billingTel');
let billingAddr = document.getElementById('billingAddr');
let shippingName = document.getElementById('shippingName');
let shippingTel = document.getElementById('shippingTel');
let shippingAddr = document.getElementById('shippingAddr');

checkBox.addEventListener('click', ()=>{
    if(checkBox.checked == true) {
        shippingName.value = billingName.value;
        shippingTel.value = billingTel.value;
        shippingAddr.value = billingAddr.value;
    } else {
        shippingName.value = '';
        shippingTel.value = '';
        shippingAddr.value = '';
    }
 })

▲ js (html 파일은 생략)

  • checked 속성으로 체크박스의 선택 유무를 true, false로 반환 받을 수 있다.
  • checked 속성이 true 즉, 체크되었다면 두 input의 값을 갖게 함
  • false라면 공란으로 바꿔줌

 

2. select-option 태그

name 속성으로 option 요소에 접근 document.form이름.option요소의이름
 

인덱스로 요소의 값에 접근 document.form이름.options[인덱스].value
 

selectedIndex: 선택된 요소의 인덱스를 반환
 
 

🔗 주문 프로그램  만들기: 체크박스 선택에 따라 총가격 변화

완성

<!DOCTYPE html>
<html lang="ko">
<head>
	<title>연습문제 1</title>
	<link rel="stylesheet" href="css/sol-1.css">
</head>
<body>
	<div id="container">
    <h1>피자 주문</h1>
		<form>
      <fieldset>
        <legend>사이즈</legend>
        <p>Large - 24000 원 </p>
      </fieldset>
      <fieldset>
        <legend>추가 주문 </legend>        
          <label><input type="checkbox" name="pickle" class="checkbx" value="800">피클(800원)</label>
          <label><input type="checkbox" name="chilly" class="checkbx" value="300">칠리 소스(300원)</label>
          <label><input type="checkbox" name="deeping" class="checkbx" value="200">디핑 소스(200원)</label>
          <label><input type="checkbox" name="stick" class="checkbx" value="4800">치즈스틱(4개, 4800원)</label>
          <label><input type="checkbox" name="salad" class="checkbx" value="2400">콘 샐러드(2400원)</label>        
      </fieldset>
      <fieldset>
        <legend>합계</legend>
        <input type="text" id="total" name="total" class="price" readonly>
      </fieldset>
		</form>	
	</div>
  <script src="js/quiz-1.js"></script>
</body>
</html>
let checkboxes = document.querySelectorAll('.checkbx');
let totalPrice =  document.querySelector('#total');
let price = 24000;

totalPrice.value = price + '원';

for(i=0; i<checkboxes.length; i++) {
    checkboxes[i].addEventListener('click', (e)=>{
        if(e.target.checked) {
            price += parseInt(e.target.value);
         
        } else {
            price -= parseInt(e.target.value);
        }
        totalPrice.value = price + '원';
    })
}

▲ js

  • 체크박스들을 querySelectorAll()을 사용하여 배열로 변수에 저장
  • for문을 이용해 배열로 저장한 체크박스들 하나하나에 이벤트 핸들러를 걸어줌 **
  • 이벤트의 타켓(e.target)에 접근해서 체크되었다면 체크박스의 값(사이드 옵션 가격)을 총 가격에 더하거나 빼줌
  • 총 가격 표시 영역에 총가격을 대입

** for문을 사용하긴 했지만 이러면 모든 체크박스에 이벤트가 걸리기 때문에 이벤트 위임을 사용하는 것이 기능적으로 더 좋다. (예시) (추후 별도 포스팅 후 링크 첨부해야지)

 

 

 

 

💭 마치며

마지막 주문 프로그램을 작성하며 조금 헤매는 바람에 이벤트 위임에 대해 완벽히 적응할 수 있게 되었다. 최근에 비슷한 기능을 리팩토링하고 포스팅도 작성한게 큰 도움이 된!!! 리팩터링 하다가 이 교육 들으러 온건데 안 그래도 자바스크립트 수업을 계속 듣다보니 자.스가 너무 재밌다 ㅎㅎ.. 더 노력하고 더 잘해야지!

댓글