console.log('I love u' && 'React!');

위 코드를 실행하면 어떤 결과 값이 출력될까? 

I live u 와 React! 모두 문자열이기 때문에 Truthy한 값이라 true and true가 되서 콘솔에 true가 출력될것 같다.

//출력된 값
React!

하지만 실행해보면 "React!"가 출력된걸 볼 수 있다. 자바스크립트에서 논리연산자는 매번 true 혹은 false를 리턴하는게 아니라 상황에 따라 양쪽 값 중 어느 한쪽을 선택하는 방식으로 동작한다.

//익숙하게 본 boolean 형태로 AND 연산자를 생각해보자
console.log(true && true); //ture
console.log(true && false); //false
console.log(false && true); //flase
console.log(false && false); //flase

AND 연산자는 양쪽 값이 모두 true인 경우에만 true를 리턴하고 어느 한쪽이라도 false일 경우 false를 리턴한다.

하지만 사실 왼쪽 값이 truthy하면 오른쪽 값을 리턴하고, 왼쪽 값이 falsy 하면 그대로 왼쪽 값을 리턴하는 방식으로 동작한다. 그래서 위에서 작성했던 문자열로 작성한 연산자에서 "React!"를 출력한 것이다. 문자열 "I love u"가 truthy 하기 때문에 오른쪽에 있는 "React!"가 리턴 된 것이다.

그렇다면 OR 연산자는?

//OR 연산자 boolean 타입
console.log(true || true); //ture
console.log(true || false); //true
console.log(false || true); //true
console.log(false || false); //false

둘 중 하나라도 true일 경우 true를 리턴하고 양쪽 다 false일 경우에 false를 리턴했다. 그런데 OR 연산자도 둘 중 하나를 선택하는 관점에서 바라보면 AND연산자와 정반대로

왼쪽 값이 truthhy 하면 그대로 왼쪽 값을 리턴하고, 왼쪽 값이 falsy하면 오른쪽 값을 리턴하는 방식으로 동작한다.

예시를 가지고 연산을 더 공부해보자.

console.log(null && undefined);
console.log(0 || true);
console.log('0' && NaN);
console.log({} || 123);
  1. null과 undefined 둘 다 falsy 값으로 flase AND false. 왼쪽 값이 falsy 하면 그대로 왼쪽 값을 리턴하기 때문에 null이 출력될것이다.
  2. 0은 falsy값, true는 말 그대로 true. false OR true. 왼쪽값이 falsy하면 오른쪽 값을 리턴하니 true가 출력될 것이다.
  3. 0이라는 문자열 true, NaN은 falsy 값. true AND false. 왼쪽 값이 true기 때문에 오른쪽 값을 리턴해 NaN이 출력될 것이다.
  4. 빈 배열과 객체 또한 truthy. 그리고 숫자 123은 true. true OR true. 왼쪽값이 truthy하면 그대로 왼쪽값을 리턴하기 때문에 {빈 객체}가 출력될 것이다.
function print(value) {
  const message = value || "Code World!";
  
  console.log(message);
};

print(); //Code World!
print('JavaScript'); //JavaScript

함수를 호출할 때 아무런 값도 전달하지 않으면 파라미터에 undefined, flasy한 값이 전달된다는 특징을 이용해서 함수를 호출할 때 전달한 값이 없다면 "Code World!"를,  전달한 값이 있을 땐 그 값을 사용하게끔 조건식처럼 활용할 수 있다.


AND 연산자와 OR 연산자 우선순위

function checkAnswer(value) {
  if (value < 10 && value > 0 && value !== 3) {
    return '정답입니다!';
  }
  return '틀렸습니다!';
};

console.log(checkAnswer(4)); // 정답입니다!

10보다는 작고, 0보다는 크지만 3은 아닐때 정답입니다를 리턴하고, 아니라면 틀렸습니다를 리턴하는 함수이다.

코드를 작성하다보면 다양한 상황을 고려하기 위해 이렇게 AND나 OR 연산자를 여러번 사용해야 할 수도 있다.

AND 연산자나 OR 연산자 중 하나만 계속해서 사용할 때는 문제 없지만, 만약 AND 연산자와 OR 연산자를 섞어서 사용할 때는 연산의 우선순위가 존재한다.

쉽게 설명해서 1 + 2 + 3 처럼 계속해서 더하기 연산자만 사용한다면 왼쪽부터 차례대로 더하면 되지만, 1 + 2 * 3 처럼 더하기와 곱하기 연산자가 섞여 있다면 연산자 우선순위를 고려해야 한다. AND 와 OR 연산자 사이에서는 AND 연산자의 우선순위가 더 높다.

console.log(true || false && false); // true
console.log((true || false) && false); // false

console.log('Coding' || NaN && false); // Coding
console.log(('Coding' || NaN) && false); // false

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

함수  (0) 2024.12.15
null 병합 연산자 (??)  (0) 2024.12.15
Falsy & Truthy  (1) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15

자바스크립트에서 다양한 값들은 맥락에 따라서 boolean 값으로 평가 될 수 있다.

빈문자열은 falsy값이지만, 빈 배열과 빈 객체는 truthy 값이다.
직접 boolean을 통해서 확인 해 볼 수도 있다.

 

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

null 병합 연산자 (??)  (0) 2024.12.15
AND와 OR 연산자 심화  (0) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14

많이는 사용되지 않지만 ES2015 이후로 6개의 데이터 타입 외에 2개의 데이터 타입이 추가되었다. 많이 활용되진 않으니 가볍게 정리를 해보자.

Symbol 심볼은 기본형 데이터 타입 중 하나로 코드 내에서 유일한 값을 가진 변수 이름을 만들 때 사용한다.

//이렇게 symbol이라는 함수를 통해 심볼 값을 만들어 낼 수도 있다.
const user = Symbol('this is a user');

//그리고 괄호 안에 심볼에 대한 설명을 붙일 수도 있다.
//이렇게 Symbol 값을 담게 된 user라는 이름의 변수는 다른 어떤 값과 비교해도 true가 될 수 없는 고유한 변수가 된다.
const user = Symbol('this is a user');

user === 'this is user'; // false
user === 'user'; // false
user === 'Symbol'; // false
user === true; // false
user === false; // false
user === 123; // false
user === 0; // false
user === null; // false
user === undefined; // false
...

//심지어 똑같은 설명을 붙인 심볼을 만들더라도 두 값을 비교하면 false가 반환된다.
const symbolA = Symbol('this is Symbol');
const symbolB = Symbol('this is Symbol');

console.log(symbolA === symbolB); // false

BigInt 는 자바스크립트에서 아주 큰 정수(integer)를 표현하기 위해 등장한 데이터 타입이다. 자바스크립트의 숫자에는 안전한 정수 표현에 한계가 있는데, 안전한 정수 표현이라는게 조금 이상하긴 하지만 약 9,000조 정도의 숫자를 넘어가면 연산에 미세한 오류가 발생한다.

//Biglnt 타입의 값은 일반 정수 마지막에 알파벳 n을 붙이거나 Biglnt라는 함수를 사용하면 된다.
console.log(9007199254740993n); // 9007199254740993n
console.log(BigInt('9007199254740993')); // 9007199254740993n

//하지만 말 그대로 큰 정수를 표현하기 위한 데이터 타입이기 때문에 소수에 대한 표현은 지원하지 않는다.
1.5n; // SyntaxError

//소수로 결과가 리턴되는 연산은 소숫점 아랫부분을 버리고 정수 형태로 리턴한다.
10n / 6n; // 1n
5n / 2n; // 2n

//그리고 Biglnt 타입 끼리만 연산이 가능하고 서로 다른 타입은 명시적으로 타입변환을 해야한다.
3n * 2; // TypeError
3n * 2n; // 6n
Number(3n) * 2; // 6

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

AND와 OR 연산자 심화  (0) 2024.12.15
Falsy & Truthy  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14
자바스크립트 이벤트  (0) 2024.12.14

1. MouseEvent.button

마우스 이벤트 객체의 버튼 프로퍼티를 활용하면, 마우스 버튼을 눌렀을 때 일어난 이벤트에 대해서 어떤 버튼을 눌러서 일어난 이벤트인지를 정확하게 알아낼 수 있다.

내용 MouseEvent.type
0 마우스 왼쪽 버튼 click 마우스 왼쪽 버튼을 눌렀을 때
1 마우스 휠 contextmenu 마우스 오른쪽 버튼 눌렀을 때
2 마우스 오른쪽 버튼 dbclick 더블클릭 (동일 버튼 두번 누르기)
3 X1 (일반적으로 브라우저 뒤로 가기 버튼) mousedown 마우스 버튼을 누른 순간
4 X2 (일반적으로 브라우저 앞으로 가기 버튼) mouseup 마우스 버튼을 눌렀다 뗀 순간

동작을 하나 하더라도 여러 이벤트가 동시에 일어날 수 있다. 예를 들어 클릭을 한번 하더라도, mousedown, mouseup, click까지 3개의 이벤트가 발생하게 된다. 이렇게 동시에 일어날땐 각각의 순서에 대해서 잘 이해하는게 중요하다. 더블클릭의 경우엔 mousedown, mouseup, click, mousedown, mouseup, click, dbclick 순으로 이벤트가 발생되기 때문에 잘 생각해보자.

+ 참고로 mouseenter, mouseleave, mouseover, mouseout, mousemove 처럼 마우스 이동과 관련된 이벤트에서는 이 값이 null이나 undefined가 아니라 0이다.


2. MouseEvent.type

이벤트 타입 설명
mousedown 마우스 버튼을 누르는 순간
mouseup 마우스 버튼을 눌렀다 떼는 순간
click 왼쪽 버튼을 클릭한 순간
dblclick 왼쪽 버튼을 빠르게 두 번 클릭한 순간
contextmenu 오른쪽 버튼을 클릭한 순간
mousemove 마우스를 움직이는 순간
mouseover 마우스 포인터가 요소 위로 올라온 순간
mouseout 마우스 포인터가 요소에서 벗어나는 순간
mouseenter 마우스 포인터가 요소 위로 올라온 순간 (버블링이 일어나지 않음)
mouseleave 마우스 포인터가 요소에서 벗어나는 순간 (버블링이 일어나지 않음)

mouseover, mouseout VS mouseenter, mouseleave

1. 버블링이 일어나지 않는다.

 

마우스를 올렸을 때의 차이점을 보자.

over에서는 해당 요소 바깥에서 안쪽으로 이동할 때도, 자식 요소 부분으로 이동할 때도 이벤트가 발생하지만, enter에서는 이벤트핸들러가 등록된 바깥 요소에서 안쪽으로 마우스가 이동할 때만 이벤트 핸들러가 동작한다.

2. 자식요소의 영역을 계산하지 않는다. (mouseenter와 mouseleave는 자식 요소의 영역을 계산하지 않는다.)

위 영상에서 mouseover는 자식요소의 영역을 구분하기 때문에 자식요소로 이동했을 때도 마우스 움직임을 카운트 했다. 반면 mouseenter는 자식요소를 구분하지 못하기 때문에 마우스를 자식요소로 움직였다 부모요소로 움직여도 움직임이 카운트 되지 않았다.


3. MouseEvent.위치프로퍼티

프로퍼티 설명

clientX, clientY 마우스 포인터의 브라우저 표시 영역(이벤트가 발생한 순간에 브라우저가 콘텐츠를 표시할 수 있는 영역)에서의 위치 client 값은 그 순간 보여지는 화면을 기준으로 계산하기 때문에 스크롤 위치와는 무관하게 항상 보여지는 화면의 좌측 상단의 모서리 위치를 (0, 0)으로 계산한다.
pageX, pageY 마우스 커서의 전체 문서 영역에서의 위치 스크롤로 인해서 보이지 않게된 화면의 영역까지 포함해서 측정한다
offsetX, offsetY 마우스 포인터의 이벤트 발생한 요소(이벤트가 발생한 target이 기준)에서의 위치 offset 값은 이벤트가 발생한 대상을 기준으로 계산하기 때문에 스크롤 위치와는 무관하게 항상 대상의 좌측 상단의 모서리 위치를 (0, 0)으로 계산한다.
screenX, screenY 마우스 포인터의 모니터 화면 영역에서의 위치  
const box1 = document.querySelector('#box1');

function onMouseMove(e) {
  console.log(`client: (${e.clientX}, ${e.clientY})`);
  console.log(`page: (${e.pageX}, ${e.pageY})`);
  console.log(`offset: (${e.offsetX}, ${e.offsetY})`);
  console.log('------------------------------------');
}

box1.addEventListener('mousemove', onMouseMove);

하늘색 요소 안에서 마우스가 움직이면 console 창에서 위치를 알려준다.

어느 일정한 위치에 도달했을 때 이벤트를 발생시키거나, 마우스가 이동한 방향을 파악하는데 활용하거나 할 수 있다.


4. MouseEvent.relatedTarget

mouseenter, mouseleave, mouseover, mouseout 이벤트에는 relatedTarget이라는 프로퍼티가 존재하는데 target 프로퍼티가 이벤트가 발생한 요소를 담고 있다면, relatedTarget 프로퍼티는 이벤트가 발생하기 직전(또는 직후)에 마우스가 위치해 있던 요소를 담고 있다.

const box2 = document.querySelector('#box2');

function printEventData(e) {
  console.log('event:', e.type);
  console.log('target:', e.target);
  console.log('relatedTarget:', e.relatedTarget);
  console.log('------------------------------------');
  if (e.target.classList.contains('cell')) {
    e.target.classList.toggle('on');
  }
}

box2.addEventListener('mouseover', printEventData);
box2.addEventListener('mouseout', printEventData);

console창을 보면 relatedTarget은 cell-4 target은 box2이다. event는 mouseover이니, 종합해보면 cell-4에서 box2로 마우스가 이동했다는걸 알 수 있다.

그리고 이 창을 보면, relatedTarget은 body, target은 box2이고, event는 mouseout이다. 종합해보면 box에서 body로 마우스가 이동했다는걸 알 수 있다.

event가 mouseover일때는 마우스가 이동하기 직전의 요소, mousout 일때는 마우스가 이동한 직후의 요소를 담고있는걸 확인할 수 있다.


5. KeyboardEvent.type

이벤트 타입 설명
keydown 키보드의 버튼을 누르는 순간
keypress 키보드의 버튼을 누르는 순간 ('a', '5' 등 출력이 가능한 키에서만 동작하며, Shift, Esc 등의 키에는 반응하지 않음)
keyup 키보드의 버튼을 눌렀다 떼는 순간

keypress는 웹 표준에서 권장하는 이벤트는 아니기 때문에 keypress 보다 keydown 사용을 추천한다.


6. KeyboardEvent.key vs KeyboardEvent.code

키보드 이벤트 객체에는 key와 code 프로퍼티가 자주 사용되는데 key는 사용자가 누른 키가 가지고 있는 값을 나타내고 code는 누른 키의 물리적인 위치를 나타낸다.


7. input 태그 다루기

이벤트 타입 설명
focusin 요소에 포커스가 되는 순간
focusout 요소에 포커스가 빠져나가는 순간
focus 요소에 포커스가 되는 순간 (버블링이 일어나지 않음)
blur 요소에 포커스가 빠져나가는 순간 (버블링이 일어나지 않음)
change 입력된 값이 바뀌는 순간 (입력이 시작되기 전 값과 완료된 후 값 사이에 차이가 있을 때 발생, focusout될 때 입력이 완료되었다고 생각해 focusout 직전에 change 이벤트가 발생한다.)
input 값이 입력되는 순간 (esc나 shift 같이 값이 입력되는것이랑 상관 없는 key에는 반응하지 않는다)
select 입력 양식의 하나가 선택되는 순간
submit 폼을 전송하는 순간

8. 스크롤 이벤트

scroll 이벤트는 보통 window 객체에 이벤트 핸들러를 등록하고 window 객체의 프로퍼티와 함께 자주 활용된다.

function printEvent(e) {
  console.log(window.scrollY);
};

window.addEventListener('scroll', printEvent);


특히 scrollY 프로퍼티를 활용하면 스크롤된 특정한 위치를 기준으로 이벤트 핸들러가 동작하게 하거나 혹은 스크롤 방향(위로 스크롤 중인지/아래로 스크롤 중인지)을 기준으로 이벤트 핸들러가 동작하게끔 활용할 수도 있다.


'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

Falsy & Truthy  (1) 2024.12.15
새로운 데이터 타입과 특징  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14
자바스크립트 이벤트  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13

마우스 이벤트

이벤트 타입 설명
mousedown 마우스 버튼을 누르는 순간
mouseup 마우스 버튼을 눌렀다 떼는 순간
click 왼쪽 버튼을 클릭한 순간
dblclick 왼쪽 버튼을 빠르게 두 번 클릭한 순간
contextmenu 오른쪽 버튼을 클릭한 순간
mousemove 마우스를 움직이는 순간
mouseover 마우스 포인터가 요소 위로 올라온 순간
mouseout 마우스 포인터가 요소에서 벗어나는 순간
mouseenter 마우스 포인터가 요소 위로 올라온 순간 (버블링이 일어나지 않음)
mouseleave 마우스 포인터가 요소에서 벗어나는 순간 (버블링이 일어나지 않음)

키보드 이벤트

이벤트 타입 설명
keydown 키보드의 버튼을 누르는 순간
keypress 키보드의 버튼을 누르는 순간 ('a', '5' 등 출력이 가능한 키에서만 동작하며, Shift, Esc 등의 키에는 반응하지 않음)
keyup 키보드의 버튼을 눌렀다 떼는 순간

포커스 이벤트

이벤트 타입 설명
focusin 요소에 포커스가 되는 순간
focusout 요소로부터 포커스가 빠져나가는 순간
focus 요소에 포커스가 되는 순간 (버블링이 일어나지 않음)
blur 요소로부터 포커스가 빠져나가는 순간 (버블링이 일어나지 않음)

입력 이벤트

이벤트 타입 설명
change 입력된 값이 바뀌는 순간
input 값이 입력되는 순간
select 입력 양식의 하나가 선택되는 순간
submit 폼을 전송하는 순간

스크롤 이벤트

이벤트 타입 설명
scroll 스크롤 바가 움직일 때

윈도우 창 이벤트

이벤트 타입 설명
resize 윈도우 사이즈를 움직일 때 발생

 

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

새로운 데이터 타입과 특징  (1) 2024.12.15
이벤트 자세히 다뤄보기  (1) 2024.12.15
자바스크립트 이벤트  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13
객체, 요소, 노드  (1) 2024.12.13

이벤트 종류 알아보기

1. 이벤트 핸들러 등록하기

HTML의 속성이나 DOM 프로퍼티를 활용해 이벤트를 등록하는 방법 외에 Element.addEventListener('type', 'handler')를 통해서 이벤트 핸들러를 등록할 수 있다. 이 방법이 이벤트 핸들러를 등록할 때 가장 권장되는 방법이다. 이렇게 하면 하나의 요소에 여러개의 독립적인 이벤트 핸들러를 등록할 수가 있다.

<body>
  <div id="content" class="btns">
    <button id="myBtn">JS Click!</button>
    <button onclick="console.log('Hello Codeit!')">HTML Click!</button>
  </div>
  <script src="index.js"></script>
</body>
// 이벤트 등록하기
let btn = document.querySelector('#myBtn');

// btn.onclick = function () {
// 	console.log('Hi Code World!');
// };
// 이렇게 dom요소에 접근에 onclick 프로퍼티를 활용하는 방법 같은 이벤트 핸들러는 한가지 문제를 가지고 있다.
// innerHTML이나 className같은 프로퍼티를 활용할 때도 프로퍼티에 어떤 값을 할당하는 방식은 
// 기존에 있던 값을 덮어쓰는 형태로 동작하기 때문에 기존에 값을 유지하면서 일부만 수정하기는 어려운 단점이 있었다.
// onclick 프로퍼티도 새로 쓰면 기존의 값을 덮어쓴다.

function event1() {
	console.log('Hi Codeit!');
}

function event2() {
	console.log('Hi again!');
}
//위에 코드를 실행시킨다면 Hi Codeit!은 나오지 않고 Hi again!만 출력된다.
//지금은 간단한 코드이지만 각 이벤트 별로 중요한 리턴 값이 있을 경우에 그 리턴 값도 다뤄야 하는 문제가 생긴다.

// elem.addEventListener(event, handler)
btn.addEventListener('click', event1);
btn.addEventListener('click', event2);

Element.addEventListener('event', 'handler')는 이벤트 타입을 문자열로 첫번째 파라미터에 입력하고, 두번째 파라미터에는 이벤트 핸들러를 전달하면 된다. 메소드를 여러번 호출하게 되도 하나의 요소에 여러개의 독립적인 이벤트 핸들러를 등록할 수 있다.

실행해보면 이벤트1과 이벤트2가 동시에 실행되는것을 확인 할 수 있다.


2. 이벤트 핸들러 삭제하기

addEventListener 메소드를 활용해서 이벤트 핸들러를 등록했다면, Element.removeEventListner('type', 'handler')를 통해서 이벤트 핸들러를 삭제할 수 있다. 등록할 때와 똑같이 파라미터를 전달하면 해당되는 이벤트 핸들러가 삭제되는 방식이다.

단, 이벤트를 삭제할 땐 반드시 이벤트를 등록할 때 외부에 함수를 만들어서 해당 함수의 이름으로 핸들러를 전달해줘야 한다.

// elem.removeEventListener(event, handler)
btn.removeEventListener('click', event2);


btn.addEventListener('click', function() {
  console.log('event3!');
});

btn.removeEventListener('click', function() {
  console.log('event3!');
});

//이렇게는 생성도, 삭제도 되지 않는다.

function event3() {
  console.log('Hi event3!');
};
//이렇게 변수에 담아서

btn.addEventListener('click', event3);
//사용을 해주어야 한다

btn.removeEventListener('click', event3);

3. 이벤트 객체 (Event Object)

웹페이지에서 이벤트가 발생하면 관련된 정보를 담은 이벤트 객체가 만들어지고 이벤트 핸들러의 첫번째 파라미터로 전달된다. 이벤트 객체는 이벤트 종류마다 가지고 있는 프로퍼티가 다르고, 이벤트에 대한 유용한 정보들을 프로퍼티로 가지고 있다.

<body>
  <div id="content" class="btns">
    <input type="text" id="myInput" placeholder="type anything">
    <button id="myBtn">click me!</button>
  </div>
  <script src="index.js"></script>
</body>
// 이벤트 객체 (Event Object)
const myInput = document.querySelector('#myInput');
const myBtn = document.querySelector('#myBtn');

function printEvent(event) { //(event) 이벤트 핸들러의 첫번째 파라미터
  console.log(event);
    event.target.style.color = 'red';
}

myInput.addEventListener('keydown', printEvent);
myBtn.addEventListener('click', printEvent);

키보드를 누를땐 키보드 이벤트가, 클릭할 땐 클릭이벤트가 출력된다.
type은 이벤트 타입이, target은 이벤트가 발생한 요소를 담고 있다.

특히나 target은 DOM 요소가 담겨 있기 때문에 event.target.style.color = 'red'; 이런식으로 이벤트가 발생했을 때 해당 요소를 수정한다거나 혹은 그 요소가 가지고 있는 속성 값들을 참조해야 되는 상황에서 유용하게 활용된다.

프로퍼티 설명
type 이벤트 이름 (click, mouseup, keydown 등)
target 이벤트가 발생한 요소
currentTarget 이벤트 핸들러가 등록된 요소
timeStamp 이벤트 발생 시각(페이지가 로드된 이후부터 경과한 밀리초)
bubbles 버블링 단계인지를 판단하는 값

4. 이벤트 버블링 (Event Bubbling)

이벤트는 전파가 된다. 어떤 요소에서 이벤트가 발생하면 해당 요소에 등록된 이벤트 핸들러가 동작하는 것뿐만 아니라 (같은 타입의 이벤트에 한해서) 부모 요소로 이벤트가 계속해서 전파되면서 각 요소에도 등록된 이벤트 핸들러가 있다면 차례로 이벤트 핸들러가 동작한다.(최상단의 윈도우 객체를 만날때까지 반복)  자식 요소에서 부모 요소로 이벤트가 전파되는 것을 이벤트 버블링(Event Bubbling)이라고 한다.

이벤트 버블링이 일어나도 이벤트 객체의 target 프로퍼티는 변하지 않고 처음 이벤트가 발생한 시작점을 담고 있다.

참고로 이벤트 버블링은 이벤트 객체의 stopPropagation 메소드로 전파를 막을 수 있다.

for (let item of items) {
  item.addEventListener('click', function(e) {
    console.log('item Event');
    console.log(e.currentTarget);
    e.stopPropagation();
  });
};

//이 코드를 쭉 살펴보자면 items에 있는 item을 파라미터로 담고, 반복문을 실행한다.
//item에 클릭했을 때 이벤트 리스너 프로퍼티를 부여한다. 어떤 함수가 실행될거냐면
//console.log에 item Event라고 나오고, console.log에 이벤트 핸들러가 등록된 요소를 출력해주고
//버블링을 멈춰줘

위 코드를 위에서부터 해석해보자면,

for of 문으로 반복문을 실행할건데 items 함수에 담긴 item, 각 li 요소를 말한다. item이 클릭되는 이벤트가 발생하면 함수를 실행해주세요, 어떤 함수? console에 'item Event'라는 문자열 출력해주시고, console에 e.currentTarget 이벤트 리스너가 연결된 (핸들러가 등록된) 요소(즉, 클릭된 li 요소)를 출력해주세요. 그리고 나서 이벤트가 부모요소로 전파되는 버블링을 멈춰주세요.

메소드 하나로 간단하게 버블링을 막을 수는 있지만 정말 필요한 경우가 아니라면 가급적 버블링을 막는 일은 피하는것이 좋다. 지금처럼 아이템 영역에서 이벤트 버블링을 막아버리면 바로 위에 있는 리스트 뿐만 아니라 모드 부모요소 입장에서 아이템 영역만큼의 이벤트를 발생시킬 범위가 사라져버리게 된다.

예를들자면 페이지 전체에 어떤 이벤트를 만들고 싶을 경우 document나 body 같은 문서 전체를 다룰 수 있는 상위 요소의이벤트 핸들러를 만들어 줄텐데 버블링이 막혀있는 구간이 존재하게 되면 당연히 클릭 했을 때 버블링이 막혀버리기 때문에 그 부분만 원하는 이벤트 결과를 얻지 못한다. 버블링을 잘 이해하고 이벤트를 설계한다면 버블링을 막을 일이 많지 않다.

+여담

버블링 외에도 캡쳐링(capturing)이라는 흐름이 존재한다. 실제 코드에서 자주 쓰이진 않는다. 캡처링 단계에서 이벤트를 발생시켜야 하는 일은 매우 드물다.

  1. 캡처링 단계 : 이벤트가 하위 요소로 전파되는 단계
  2. 타깃 단계 : 이벤트가 실제 타킷 요소에 전달되는 단계 = 이벤트 객체의 target 프로퍼티가 되는 요소에 등록되어있던 이벤트 핸들러가 동작하는 단계 (가장 처음 이벤트 핸들러가 동작하는 순간)
  3. 버블링 단계 : 이벤트가 상위 요소로 전파되는 단계

캡처링은 이벤트가 발생하면 가장 먼저, 그리고 버블링의 반대방향으로 진행되는 이벤트 전파 방식이다.

이벤트가 발생하면 가장먼저 window 객체에서부터 target까지 이벤트 전파가 일어나고, (캡쳐링) 타겟에 도달하면 등록된 이벤트 핸들러가 동작하고, (타깃) 이후에 다시 window 객체로 이벤트가 전파된다. (버블링)

자주 사용되진 않지만 그럼에도 불구하고 캡처링 단계에서 이벤트 핸들러를 동작해야 된다면? addEventListener에 세번째 프로퍼티에 true 또는 { capture:true }를 전달하면 된다.

for (let elem of document.querySelectorAll('*')) {
  elem.addEventListener("click", e => alert(`캡쳐링 단계: ${elem.tagName}`), true);
  elem.addEventListener("click", e => alert(`버블링 단계: ${elem.tagName}`));
}

5. 이벤트 위임 (Event Delegation)

버블링 개념을 활용하면 자식 요소 각각에 이벤트 핸들러를 하나씩 등록할 필요 없이 부모 요소에서 한 번에 자식 요소들에 발생한 이벤트를 관리할 수도 있다.

이렇게 이벤트를 다루는 방식을 자식 요소의 이벤트를 부모 요소에 위임한다고 해서 이벤트 위임(Event Delegation)이라고 한다.

예시 상황

만약 이미 있는 요소들에 이벤트 핸들러를 다 설정했는데 새로운 아이템을 추가 하는 상황이 발생하게 되면 추가된 아이템에는 이벤트 핸들러가 동작하지 않는다. 그래서 매번 추가할 때마다 이벤트 핸들러를 새로 등록해야 되는 문제가 있다. 이럴땐 이벤트 버블링을 활용하자.

자식요소에서 발생하는 이벤트를 부모요소에서 다루는 방식 = 이벤트 위임

리스트에 이벤트 핸들러를 하나밖에 등록하지 않았지만 아이템 각각의 이벤트 핸들러를 등록한것처럼 동작하고, 나중에 추가한 아이템도 이벤트 핸들러가 잘 동작한다. 하지만 그래도 문제가 한가지 있다. 자식요소를 제외한 온전한 부모요소를 클릭해도 이벤트 핸들러가 동작된다. item(li)영역을 벗어난 list(ul)영역을 선택해도 done class가 toggle되어 이벤트가 발생한다. 그래서 이벤트 위임을 할때는 명확하게 원하는 요소에서 의도한 동작이 일어나게끔 따로 처리를 해줘야한다.

// 이벤트 위임 (Event Delegation)
const list = document.querySelector('#list');

list.addEventListener('click', function(e) {
	// target프로퍼티의 tagName이 LI인지 확인하거나
	// if (e.target.tagName === 'LI')
  if (e.target.classList.contains('item')) {
    //classList의 contains 메소드를 이용해 item이라는 class를 가지고 있는지 확인하면 해결할 수 있다.
    e.target.classList.toggle('done');
  }
});

const li = document.createElement('li');
li.classList.add('item');
li.textContent = '일기 쓰기';
list.append(li);

tagName 프로퍼티는 해당 요소의 태그 이름 값을 대문자로 담고 있는 프로퍼티이고, classList의 contains 메소드는 파라미터로 전달하는 값이 해당 요소의 클래스 속성에 있는지 확인해 불린형태로 결과를 리턴해주는 메소드이다.


6. 브라우저의 기본 동작

브라우저에는 각 태그별 혹은 상황별로 기본적으로 약속된 동작들이 있다.

예를 들어 마우스 오른쪽 버튼을 클릭하면 상황에 맞는 메뉴 창이 뜬다거나, input 태그에 커서를 두고 키보드 키를 누르면 해당 값이 입력된다거나 하는. 만약 이런 동작들을 막고 싶다면 이벤트 객체의 preventDefault 메소드를 통해 막을 수가 있다.

<body>
  <a id="link" href="https://www.codeit.kr/">Link</a>
  <input type="checkbox" id="checkbox">
  <input type="text" id="input" placeholder="input">
  <p id="text">
    I Love Codeit!
  </p>
  <script src="index.js"></script>
</body>
// 브라우저의 기본 동작
const link = document.querySelector('#link');
const checkbox = document.querySelector('#checkbox');
const input = document.querySelector('#input');
const text = document.querySelector('#text');

//event.preventDefault 브라우저의 기본적인 동작을 막는 메소드
link.addEventListener('click', function(e) {
  e.preventDefault(); //링크가 클릭되면 기본동작(href속성 값으로 넘어가는것)이 되지 않고
  alert('지금은 이동할 수 없습니다.'); //경고창을 띄워주세요.
});

//키도브 이벤트로 활용해보기
input.addEventListener('keydown', function(e) {
  if (!checkbox.checked) { //체크박스가 체크되지 않으면
    e.preventDefault(); //input태그에서 키가 입력되지 않게 막아주시고
    alert('체크박스를 먼저 체크해 주세요.'); //경고창을 띄워주세요.
  }
});

//마우스 우클릭 방지해보기, 마우스 우클릭 = contextmenu 라는 타입으로 이벤트가 발생된다.
text.addEventListener('contextmenu', function(e) {
	e.preventDefault(); //다른곳에서는 우클릭이 되지만 텍스트 위에서 우클릭을 할 경우
	alert('마우스 오른쪽 클릭은 사용할 수 없습니다.'); //경고창이 띄워진다.
});

//문서 전체에 우클릭 방지를 하고 싶다면 text를 document로 바꿔주면 된다.

하지만 각 HTML 태그들이 가지고 있는 고유한 역할과 의미를 훼손하게 될 수도 있기 때문에 꼭 필요한 경우에만 주의해서 사용해야 한다.


'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

이벤트 자세히 다뤄보기  (1) 2024.12.15
어떤 이벤트들이 있을까?  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13
객체, 요소, 노드  (1) 2024.12.13
이벤트와 클릭  (0) 2024.12.13

1. window 객체

window 객체는 브라우저 창을 대변하면서 자바스크립트에서 최상단에 존재하는 객체이다.
자바스크립트 코드 어느 곳에서나 항상 접근할 수 있는 객체이기 때문에 전역 객체, Global Object라고 부르고,
어떤 프로퍼티나 메소드를 사용하든 결국 전역 객체 내부의 것이기 때문에 앞에 window.을 생략할 수도 있다.

window.console.log(window);
window.console.log(window.innerWidth);
window.console.log(window.innerHeight);

2. DOM (Document Object Model, 문서 객체 모델)

간단하게 표현하면 웹 페이지(웹 문서)에 나타나는 HTML 문서 전체를 객체로 표현한 것으로 생각하면 되는데, 이때 각 객체를 노드(Node)라는 용어로 표현하고, 태그는 요소 노드, 문자는 텍스트 노드로 구분된다.

console.log(document);

출력되는 화면, DOM을 직접적으로 접근하게 되면 객체가 아니라 DOM에 해당하는 HTML이 출력된다.

자바스크립트에서 사용해봤던 document 객체가 이 웹 문서의 최상단 객체로 진입점의 역할을 한다.
그래서 document 객체를 활용하면 웹 페이지 내부에 무엇이든 수정할 수 있고, 새로운 콘텐츠를 만들 수도 있다.

3. DOM 트리

DOM 트리

HTML의 계층 구조는 DOM에서도 반영되는데 이러한 계층구조를 나무에 비유해서 DOM 트리라고 부른다.
각 노드 간의 관계는 부모, 자식, 형제라는 용어로 표현한다. DOM 트리의 구조를 잘 파악하고 필요한 노드에 좀 더 자유롭게 접근하기 위해서는 관계(부모, 자식, 형제)를 잘 이해하는것이 중요하다.

태그를 표현하는 노드를 요소 노드(Element Node)라고 부르고, 문자를 표현하는 노드를 텍스트 노트(Text Node)라고 부른다. 일반적으로 텍스트 노드들은 요소 노드의 자식 노드가 되고, 따로 자식 노드를 가질 수 없기 때문에 나무의 잎사귀에 비유해서 잎 노드(Leaf Node)라고 한다. 

4. DOM 이동 시 활용 가능한 프로퍼티

프로퍼티 유형 결과
element.children 자식 요소 노드 element의 자식 요소 모음(HTMLCollection)
element.firstElementChild 자식 요소 노드 element의 첫 번째 자식 요소 하나
element.lastElementChild 자식 요소 노드 element의 마지막 자식 요소 하나
element.parentElement 부모 요소 노드 element의 부모 요소 하나
element.previousElementSibling 형제 요소 노드 element의 이전(previous) 혹은 좌측(left)에 있는 요소 하나
element.nextElementSibling 형제 요소 노드 element의 다음(next) 혹은 우측(right)에 있는 요소 하나
node.childNodes 자식 노드 node의 자식 노드 모음(NodeList)
node.firstChild 자식 노드 node의 첫 번째 자식 노드 하나
node.lastChild 자식 노드 node의 마지막 자식 노드 하나
node.parentNode 부모 노드 node의 부모 요소 하나
node.previousSibling 형제 노드 node의 이전(previous) 혹은 좌측(left)에 있는 노드 하나
node.nextSibling 형제 노드 node의 다음(next) 혹은 우측(right)에 있는 노드 하나

요소 노드에 대한 이동 프로퍼티  /  모든 노드에 대한 이동 프로퍼티

<body>
  <div id="content">
    <h2 id="title-1">Cat-1</h2>
    <ul id="list-1">
      <li></li>
      <li></li>
      <li></li>
    </ul>
    <h2 id="title-2">Cat-2</h2>
    <ul id="list-2">
      <li></li>
      <li></li>
      <li></li>
      <li></li>
    </ul>
  </div>
  <script src="index.js"></script>
</body>
//DOM 트리 여행하기
const myTag = document.querySelector('#content');

console.log(myTag);

//자식 요소 노드
console.log(myTag.children); 
console.log(myTag.children[1]);  //자식 요소 노드의 1번째 인덱스에 오는 요소. list-1
console.log(myTag.firstElementChild);  //첫번째 자식 요소. title-1
console.log(myTag.lastElementChild);  //마지막 자식 요소. list-2

//부모 요소 노드
console.log(myTag.parentElement); //body태그가 선택된다.

//형제 요소 노드
console.log(myTag.previousElementSibling); //이전, 좌측에 있는 형제. null
console.log(myTag.nextElementSibling); //다음, 우측에 있는 형제. script

5. 주요 요소 노드 프로퍼티

프로퍼티 내용 참고사항
element.innerHTML 요소 노드 내부의 HTML코드 문자열로 리턴
(줄바꿈이나 들여쓰기, 띄어쓰기도 모두 포함)
요소 안의 정보를 확인할 수도 있지만, 내부의 HTML 자체를 수정할 때 좀 더 자주 활용
element.outerHTML 요소 노드 자체의 전체적인 HTML 코드를 문자열로 리턴 (해당요소 포함)
(줄바꿈이나 들여쓰기, 띄어쓰기도 모두 포함)
outerHTML은 새로운 값을 할당하면 요소 자체가 교체되어 버리기 때문에 주의 (수정X 처음 선택한 요소는 사라진다.)
element.textContent 요소 노드 내부의 내용들 중에서 HTML을 제외하고 텍스트만 리턴 textContent는 말그대로 텍스트만 다루기 때문에 HTML태그를 쓰더라도 모두 텍스트로 처리됨
<body>
  <div id="content">
    <h2 id="title-1">Cat-1</h2>
    <ul id="list-1">
      <li>wonderful</li>
      <li>beautiful</li>
      <li>great</li>
    </ul>
    <h2 id="title-2">Cat-2</h2>
    <ul id="list-2">
      <li>dog</li>
      <li>cat</li>
      <li>monkey</li>
      <li>pig</li>
    </ul>
  </div>
  <script src="index.js"></script>
</body>
//요소 노드 주요 프로퍼티
const myTag = document.querySelector('#list-1');

//innerHTML
console.log(myTag.innerHTML);
myTag.innerHTML = '<li>Exotic</li>';
myTag.innerHTML += '<li>Exotic</li>'; //덧셈 할당 연산자를 통해 기존 HTML 마지막 부분에 추가할 수도 있다.

//outerHTML
console.log(myTag.outerHTML);
myTag.outerHTML = '<ul id="new-list"><li>Exotic</li></ul>';
//수정이 아닌 교체. 처음 선택한 요소는 사라지고 새로 HTML을 만드는 것이다.

//textContext
console.log(myTag.textContext);
myTag.textContext = 'new text!';
//새로운 값을 할당하면 innerHTML과 마찬가지로 내부의 값을 완전히 새로운 값으로 교체

innerHTML 수정 전(좌)과 수정 후(우)

6. 요소 노드 다루기

1. 요소 노드 만들기: document.createElement('태그이름')
2. 요소 노드 꾸미기: element.textContent, element.innerHTML, ...
3. 요소 노드 추가 혹은 이동하기: element.prepend, element.append, element.after, element.before
4. 요소 노드 삭제하기: element.remove()

<body>
  <div>
    <h1>오늘 할 일</h1>
	<ol id="today">
	  <li>자바스크립트 공부</li>
	  <li>고양이 화장실 청소</li>
	  <li>고양이 장난감 쇼핑</li>
	</ol>
	<h1>내일 할 일</h1>
	<ol id="tomorrow">
	  <li>자바스크립트 공부</li>
	  <li>뮤지컬 공연 예매</li>
	  <li>유튜브 시청</li>
	</ol>
  </div>
  <script src="index.js"></script>
</body>
// 요소 노드 추가하기
const tomorrow = document.querySelector('#tomorrow');

// 1. 요소 노드 만들기: document.createElement('태그이름')
const first = document.createElement('li');

// 2. 요소 노드 꾸미기: textContent, innerHTML, ...
first.textContent = '처음';

// 3. 요소 노드 추가하기: NODE.prepend, append, after, before
tomorrow.prepend(first);

const last = document.createElement('li');
last.textContent = '마지막';
tomorrow.append(last);

const last = document.createElement('p');
prev.textContent = '이전';
tomorrow.before('문자열');

const last = document.createElement('p');
next.textContent = '다음';
tomorrow.after(next);
// 노드 삭제와 이동
const today = document.querySelector('#today');
const tomorrow = document.querySelector('#tomorrow');

// 노드 삭제하기: Node.remove()
tomorrow.remove();
today.children[2].remove();

// 노드 이동하기: prepend, append, before, after
today.append(tomorrow.children[1]);
//today에 tomorrow의 1번째 인덱스 자식요소를 가져와줘 (today의 맨마지막에 추가된다.)
tomorrow.children[1].after(today.children[1]);
//tomorrow에 자식요소 1번째 인덱스 뒤에 today 자식요소 1번째 인덱스를 옮겨줘
tomorrow.children[2].before(today.children[1]);
//tomorrow의 자식요소 2번째 인덱스 앞에 today 자식요소 1번째 인덱스를 옮겨줘 
tomorrow.lastElementChild.before(today.children[1]);
//tomorrow의 마지막 자식 요소의 앞에 today 자식요소 1번째 인덱스 가져와줘

7. HTML 속성 다루기

대부분의 HTML 속성은 DOM 객체의 프로퍼티로 변환이 된다. 하지만, 표준 속성이 아닌 경우에는 프로퍼티로 변환이 안 되는데, 아래 메소드를 활용하면 표준이 아닌 HTML 속성들도 다룰 수 있다.

1. 속성에 접근하기: element.getAttribute('속성')
2. 속성 추가(수정)하기: element.setAttribute('속성', '값')
3. 속성 제거하기: element.removeAttribute('속성')

// HTML 속성 (HTML attribute)
const tomorrow = document.querySelector('#tomorrow');
const item = tomorrow.firstElementChild;
const link = item.firstElementChild;

// elem.getAttribute('속성'): 속성에 접근하기
console.log(tomorrow.getAttribute('href'));
console.log(item.getAttribute('class')); //getAttribute를 활용할 때는 class라는 문자열을 그대로 전달

// elem.setAttribute('속성', '값'): 속성 추가(수정)하기
tomorrow.setAttribute('class', 'list'); // 추가
link.setAttribute('href', 'https://www.codeit.kr'); // 수정

// elem.removeAttribute('속성'): 속성 제거하기
tomorrow.removeAttribute('href'); 
tomorrow.removeAttribute('class');

// id 속성
console.log(tomorrow);
console.log(tomorrow.id);

// class 속성
console.log(item);
console.log(item.className); //프로퍼티 이름으로 접근할때는 className이지만

// href 속성
console.log(link);
console.log(link.href);
console.log(tomorrow.href);

8. 스타일 다루기

자바스크립트로 태그의 스타일을 다루는 방법에는 크게 두 가지가 있다.

1. style 프로퍼티 활용하기: element.style.styleName = 'value';
2. class 변경을 통해 간접적으로 스타일 적용하기: element.className, element.classList

‼️style 프로퍼티를 통해서 css 속성을 다룰 때 여러 단어를 이어서 만든 속성은 카멜 표기법을 사용해야한다.

// 스타일 다루기
const today = document.querySelector('#today');
const tomorrow = document.querySelector('#tomorrow');

// style 프로퍼티
today.children[0].style.textDecoration = 'line-through';
today.children[0].style.backgroundColor = '#DDDDDD';
today.children[2].style.textDecoration = 'line-through';
today.children[2].style.backgroundColor = '#DDDDDD';

style 프로퍼티를 활용하면 태그에 직접적으로 적용되어 버리기 때문에 스타일 우선순위가 높아진다는 문제가 있다.
그래서 style 프로퍼티를 사용하는 것보다 태그의 클래스를 변경하는 방식이 조금 더 권장되는 방식이다.

css에 미리 스타일을 지정해두고,

.done {
  opacity: 0.5;
  text-decoration: line-through;
}
// elem.className
today.children[1].className = 'done';

className 프로퍼티에 새로운 값을 할당해서 코드를 실행해보면, done 클래스에 미리 작성해둔 스타일이 입혀지는 방식인 것이다. 다만 한가지 문제는 className 프로퍼티로 값을 변경하게 되면 클래스 속성 값 전체가 바뀌게 된다. 기존에 있던 class는 사라지고 새로 부여한 class만 남는다는 뜻!

하지만 상황에 따라서 기존에 class는 그대로 둔 채로 새로 추가 하고 싶을 수도 있을 것이다. 그럴 때는 classList를 사용하면 된다.

// elem.classList
console.log(today.classList);
console.log(today.children[1].classList);

// elem.classList: add, remove, toggle
const item = tomorrow.children[1];
item.classList.add('done'); //추가
item.classList.remove('done'); //삭제
item.classList.toggle('done'); //있으면 제거, 없으면 추가

//add와 remove는 여러개의 파라미터 값을 줄 수 있다.
item.classList.add('done', 'other');

//toggle은 여러개의 파라미터는 줄 수 없고, 두번 째 파라미터가 boolean형태의 값을 전달 받아서 
//true일 땐 add의 기능만, fales일 땐 remove의 기능만하도록 toggle의 기능을 강제하는 역할을 한다.
item.classList.toggle('done', false);

classList는 클래스의 속성 값을 유사배열로 다루는 프로퍼티이다. 클래스 속성을 하나씩 다룰 수 있는 것 뿐만 아니라 add, remove, toggle이라는 메소드도 활용할 수 있다.

위 코드의 classList 출력(consol.log)값

메소드 내용 참고사항
classList.add 클래스 추가하기 여러 개의 값을 전달하면 여러 클래스 추가 가능
classList.remove 클래스 삭제하기 여러 개의 값을 전달하면 여러 클래스 삭제 가능
classList.toggle 클래스 없으면 추가, 있으면 삭제하기 하나의 값만 적용 가능하고, 두 번째 파라미터로 추가 또는 삭제 기능을 강제할 수 있음

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

자바스크립트 이벤트  (0) 2024.12.14
console.log와 console.dir의 차이  (0) 2024.12.13
이벤트와 클릭  (0) 2024.12.13
태그 선택하기  (0) 2024.12.12
조건문  (0) 2024.01.20

이벤트 : 웹 페이지에서 발생하는 대부분의 일. ex)마우스를 움직이는 일, 클릭, 스크롤, 키보드 입력 등

<body>
  <div>
    <button id="myBtn">Click!</button>
  </div>
</body>
// 이벤트(Event)와 버튼 클릭
const btn = document.querySelector('#myBtn');

// 이벤트 핸들링(Event Handling)
btn.onclick = function() { // 이벤트 핸들러(Event Handler)
  console.log('Hello Codeit!');
};

해당 태그를 변수로 담고, onclick이라는 프로퍼티에 함수를 할당해주면 버튼에 대한 클릭이벤트에 원하는 동작을 할 수 있다.

이벤트 핸들링 : 이벤트가 발생했을 때 어떤 특별한 동작을 하도록 이벤트를 다루는 것

이벤트 핸들러 : 구체적인 동작들을 코드로 표현한 함수 부분

그리고 잘 사용하진 않지만 HTML에서 바로 이벤트 핸들러를 등록 할 수도 있다.

<body>
  <div>
    <button id="myBtn" onclick="console.log('Hello Codeit!')">Click!</button>
  </div>
</body>

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

console.log와 console.dir의 차이  (0) 2024.12.13
객체, 요소, 노드  (1) 2024.12.13
태그 선택하기  (0) 2024.12.12
조건문  (0) 2024.01.20
함수  (0) 2024.01.20

ID로 선택하기

//id로 태그 선택하기
document.getElementById('Id');

ID속성을 통해서 어떤 요소를 가져 오겠다는 뜻이다. id 값을 문자열로 전달해 주게 되면 해당되는 태그가 선택되는 원리이다.

const myTag = document.getElementById('myNumber');
console.log(myTag);

//출력되는 값, HTML에 적혀 있는 태그가 보인다.
<div id="myNumber">0</div>

뿐만 아니라 자식요소가 있는 부모요소를 선택하게 되면 부모태그 요소에 있는 모든 내용들이 표시 되고, 존재하지 않는 태그를 선택하게 되면 null값이 리턴된다.

장점 : 필요한 태그를 명확하게 선택할 수 있다.


Class로 선택하기

//class로 태그 선택하기
document.getElementsByClassName('class');

class는 id와는 다르게 여러 태그에 붙을 수 있어 Element"s"로 작성해야 하니 주의하자.

const myTags = document.getElementsByClassName('color-btn');
console.log(myTags);

출력된 화면

"color-btn" 클래스가 있는 모든 태그들이  대괄호로 감싸져 배열의 형태로 선택됐다. length 프로퍼티도 있어서 배열 같지만, 완벽한 배열은 아니다. 배열을 다룰 때 사용하는 splice나 push 같은 메소드는 사용할 수가 없기 때문에 배열이라고 부르기는 어렵다.

console.log(myTags[1]);
console.log(myTags.length);

for (let tag of myTags) {
  console.log(tag);
}

배열 메소드는 사용할 수 없지만 대괄호 표기법으로 1번 인덱스에 접근한다거나, length 프로퍼티를 활용할 수 있고, for...of 문을 사용하는데에는 아무런 문제가 없다.

+ 여러개의 값을 담고 있는 배열과 형태는 유사하지만 배열의 메소드는 사용할 수 없는 것들을 가리켜서 유사배열(Array-Like Object)이라고 한다.

그리고 클래스가 하나 밖에 없는 값에 접근하려고 하면, 태그가 하나 밖에 없으니 딱 그 태그가 선택될거라고 생각할 수 있다. 확인을 해보면 요소가 한개가 들어있는 HTMLCollection이 출력되기 때문에 그 태그를 선택하려면 [0] 번째 인덱스에 접근 해야 된다.

존재하지 않는 태그를 선택하게 되면 null값이 리턴되던 id 태그 선택과는 다르게 비어있는 HTMLCollection이 출력된다.

console.log(myTags === null); //false, 비어있는 HTMLCollection은 null값과는 다르다.
console.log(myTags.length); //0

+ 여담

유사배열의 특징

  1. 숫자 형태의 indexing이 가능하다. : 유사배열도 배열과 마찬가지로 각 요소에 0부터 시작하는 숫자 형태의 index가 있어야 한다.
  2. length 프로퍼티가 있다. : 숫자 형태의 index가 있더라도 length 프로퍼티가 없다면 유사배열이라기 보다 그냥 숫자 형태의 key로 구성된 일반적인 객체라고 볼 수 있다.
  3. 배열의 기본메소드를 사용할 수 없다. : 인덱싱을 통해 유사배열의 요소에 접근은 쉽지만 수정이나 삭제는 까다롭다. 그래서 내부 요소들은 배열처럼 다룰 수 있으면서 배열의 메소드 사용을 막고싶거나 일반 배열에는 없는 특별한 메소드를 제공하고 싶을 때 유사 배열을 활용한다.
  4. Array.isArray(유사배열)은 false다. : 유사 배열은 배열과 비슷하지만 배열이 아니기 때문에 결과값이 false이다.

태그 이름으로 선택하기

//태그 이름으로 태그 선택하기
document.getElementsByTagName('태그이름');

class와 마찬가지로 Element"s"로 작성해야 하니 주의! 실행결과 역시 class와 똑같이 HTMLCollection을 리턴한다.

const allTags = document.getElementsByTagName('*');

css선택자처럼 '*' 값을 전달하게 되면 모든 태그를 선택할 수도 있다. 하지만 명확한 의도 없이 많은 요소를 한꺼번에 다루면 예상치 못한 실수를 할 수 있으니 주의하자. 많이 사용되는 메소드는 아니다.


CSS 선택자로 선택하기

//css 선택자로 태그 선택하기
document.querySelector('css선택자');

ID 는 #, class는 .을 이용해서 작성할 수 있다.

const myTag = document.querySelector('#myNumber');
console.log(myTag);

const myTag2 = document.getElementById('myNumber');
console.log(myTag2);

위 코드는 같은 값을 출력한다. #과 getElementById는 ID 속성을 가진 태그를 가져온다.

const myTag = document.querySelector('.color-btn');
console.log(myTag);

출력된 화면

HTML Collection과 같은유사배열이 출력될 것 같았지만, color-btn 클래스가 붙어있는 태그들 중에 가장 첫번째 요소(0번째 인덱스)가 선택되었다. 만약 같은 class를 가진 태그 여러개를 가져오고 싶다면 querySelectorAll 메소드를 활용할 수 있다.

const myTags = document.querySelectorAll('.color-btn');
console.log(myTags);

const myTags2 = document.getElementsByClassName('color-btn');
console.log(myTags2);

출력되는 화면

document.querySelectorAll('.color-btn')는 NodeList가 출력됐고, getElementsByClassName('color-btn')는 HTML Collection이 출력됐다. 뭔가 이름은 조금 다르지만 안에 있는 요소는 동일하다.

+존재하지 않는 요소를 선택할 경우 querySelector는 (id 선택과 마찬가지로) null값을, querySelectorAll은 비어있는 NodeList를 리턴한다. 


 

'코린이 개념잡기 > JavaScript' 카테고리의 다른 글

객체, 요소, 노드  (1) 2024.12.13
이벤트와 클릭  (0) 2024.12.13
조건문  (0) 2024.01.20
함수  (0) 2024.01.20
예약어  (0) 2024.01.19

+ Recent posts