인터랙티브 JavaScript
id로 태그 선택
const 변수명 = document.getElementById('id명');
HTML 문서에 준재하지 않는 id값으로 태그를 선택하면 null이 나온다.
class로 태그 선택
const 변수명 = document.getElementsByClassName('class명');
태그의 순서는 깊이와 관계없이 위에서부터 차례대로이다.
변수명[1](숫자 형태의 indexing), 변수명.length, for..of문이 사용 가능하다.
배열의 형태와 유사하지만 배열의 메소드는 사용이 불가능하다.
이런 것들을 유사배열(Array-Like Object)이라고 한다.
HTML 문서에 준재하지 않는 class값으로 태그를 선택하면 HTMLCollection[]이렇게 빈 HTMLCollecion이 출력된다. length는 0이고 null과 다른 값이다.
클래스가 하나밖에 없는 값에 접근하려고 했을때
document.getElementsByClassName('class명');
이렇게 하면 태그가 하나밖에 없으니 그 태그가 바로 선택될 것이라고 생각할 수 있지만 아니다. 요소 한가지가 들어있는 HTMLCollection이 출력된다. 따라서 그 태그를 선택하려면 변수명[0]을 해야한다.
태그 이름으로 선택
css 선택자로 태그 선택
const myTag = document.querySelector('#myNumber');
const myTag2 = document.getElementsById('myNumber');
이 두개는 같은 결과를 출력한다.
const myTag = document.querySelector('.color-btn');
이것은 color-btn을 클래스로 가지고 있는 첫번째 요소만을 출력한다.
const myTags = document.querySelectorAll('.color-btn');
이렇게 해야 모든 요소를 출력한다.
NodeList라는 유사배열을 출력한다.
이벤트와 버튼 클릭
const btn = document.querySelector('#myBtn');
//이벤트 핸들링(Event Handling)
btn.onclick = function () { //이벤트 핸들러(Event Handler), 이벤트 리스너
console.log('Hello Codeit!');
}
브라우저와 JavaScript
window (전역객체 Global Object)
console.log(window); //윈도우의 내장 객체들이 나온다
DOM (Document Object Model / 문서 객체 모델)
console.log(document); //html태그가 출력된다(값에 중점)
console.log(typeof document); // object
console.dir(document); // 객체 형태로 다양한 프로퍼티들이 출력된다(객체의 속성에 중점)
DOM 트리
각각의 태그들을 노드라고 부르며 부모노드, 자식노드, 형제노드 관계에 있다.
Node Type으로는 요소 노드(Element Node)와 텍스트 노드(Text Node) 가있다. 텍스트 노드는 요소 노드의 자식 노드이며 자식 노드를 가질 수 없다. 그리고 리프 노드(Leaf Node)라고도 불린다.
DOM 트리 접근 (요소 노드)
const myTag = document.querySelector('content');
console.log(myTag)
//형제 요소 노드
console.log(myTag.previousElementSibling);
console.log(myTag.nextElementSibling);
//부모 요소 노드
console.log(myTag.parentElement);
//자식 요소 노드
console.log(myTag.children[1]);
console.log(myTag.firstElementChild);
console.log(myTag.lastElementChild);
myTag.parentElement.nextElementSibling);
이런 식으로 접근도 가능하다.
요소 노드 프로퍼티
innerHTML(줄바꿈, 들여쓰기 모두 포함된다, 요소 노드 내부의 HTML 코드를 문자열로 리턴해 준다.)
console.log(myTag.innerHTML);
myTag.innerHTML = '< li>Exotic< /li>';
이렇게 하면 기존 값이 수정된다.(기존 값이 완전히 새로운 값으로 교체)
myTag.innerHTML += '< li>Exotic< /li>';
이렇게 하면 기존의 것에서 추가도 가능하다.
outerHTML(줄바꿈, 들여쓰기 모두 포함된다, 해당 요소를 포함한 전체 HTML코드를 문자열로 리턴해준다)
console.log(myTag.outerHTML);
myTag.outerHTML = '< ul id="new-list">< li>Exotic< /li>< /ul>'
outerHTML은 해당요소 전체를 가리키는 특성 때문에 요소 자체가 새로운 요소로 교체되는 결과를 얻게된다.(수정 x)
textContent(줄바꿈, 들여쓰기 모두 포함된다, 요소 안에 있는 내용들 중에서 HTML을 제외한 text만 가져온다)
console.log(myTag.textContent);
myTag.textContent = 'new text!';
innerHTML과 마찬가지로 수정한다.(기존 값이 완전히 새로운 값으로 교체)
특수문자도 text로 처리하기 때문에 HTML코드처럼 작성해도 그대로 text로 나온다
요소 노드 추가
const tomorrow = document.querySelector('#tomorrow');
요소 노드 추가하기: NODE.prepend, append, after, before
tomorrow.prepend(first);
const last = document.createElement('li');
last.textContent = '마지막';
tomorrow.append(last);
const prev = document.createElement('p');
prev.textContent = '이전';
tomorrow.before(prev); // tomorrow.before('문자열'); 이런식으로도 가능
const next = document.createElement('p');
next.textContent = '다음';
tomorrow.append(next);
여러개 전달이 가능하다
ex)tomorrow.befor('문자열', prev);
입력 순서대로 문자열이 위에 그 다음에 prev가 출력된다
노드 삭제와 이동
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]);
tomorrow.children[1].after(today.children[1]);
tomorrow.children[2].before(today.children[1]);
tomorrow.lastElementChild.before(today.children[1]);
HTML 속성 다루기
const tomorrow = document.querySelector('#tomorrow');
const item = tomorrow.firstElementChild;
const link = item.firstElementChild;
스타일 다루기
style 프로퍼티
today.children[0].style.textDecoration = 'line-through';
today.children[0].style.backgroundColor = '#DDDDDD';
(text-decoration -> textDecration 이렇게 카멜을 사용해야 한다)
elem.className
today.children[1].className = 'done';
(style.css에 있는 done이라는 클래스를 적용시키는 것)
elem.classList: add, remove, toggle(없으면 추가 있으면 삭제)
(class의 속성값을 유사배열로 다루는 프로퍼티)
today.classList;
today.children[1].classList;
const item = tomorrow.children[1];
item.classList.add('done'); (item.classList.add('done', 'other');)이렇게 여러 파라미터 전달 가능
item.classList.remove('done'); (remove도 여러개 파라미터 전달이 가능하다)
item.classList.toggle('done');
이벤트
let btn = document.querySelector('#myBtn');
function event1() {
console.log('Hi Codeit!');
}
function event2() {
console.log('Hi again!');
}
elem.addEventListener(event, handler)
btn.addEventListener('click', event1())
//이렇게 event1()를 하게 되면 event1 함수의 리턴값이라는 표현이기 때문에 빼야한다
btn.addEventListener('click', event2)
elem.removeEventListener(event, handler)
btn.removeEventListener('click', event2)
(removeEventListener 메소드는 파라미터로 전달하는 타입과 이벤트 핸들러가 addEventListener 메소드로 등록할 때와 동일 할 때만 이벤트 핸들러를 삭제할 수 있다.)
이벤트
마우스 이벤트 (MouseEvent.type) / 각 이벤트의 순서가 있다
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 윈도우 사이즈를 움직일 때 발생
이벤트 객체(이벤트 핸들러가 되는 함수의 첫 파라미터는 자동으로 이벤트 객체가 전달된다.)
const myInput = document.querySelector('#myInput');
const myBtn = document.querySelector('#myBtn');
function printEvent(envnt) { //(event)대신 (e)로 쓰기도 한다
console.log(event);
}
myInput.addEventListener('keydown', printEvent);
myBtn.addEventListener('click', printEvent);
const content = document.querySelector('#content');
const title = document.querySelector('#title');
const list = document.querySelector('#list');
const items = document.querySelector('.item');
content.addEventListener('click', function(e) {
console.log('content Event');
console.log(e.target);
});
for(let item of items) {
item.addEventListener('click', function(e){
console.log('item Event');
console.log(e.target);
});
}
target은 각 이벤트 핸들러가 동작할 때 해당 핸들러가 등록된 요소가 타겟이 될 것이라고 오해하는 경우가 있는데 이것은 아니다. 타겟은 변하지 않는 것을 확인할 수 있다.
이벤트 버블링이 일어나도 이벤트 객체의 타겟 프로퍼티는 변하지 않고 처음 이벤트가 발생한 시작점을 담고 있다.
이벤트 핸들러가 등록된 요소에 접근하고 싶은 경우에는 e.currentTarget을 사용하면 된다. 그러면 이벤트 핸들러가 동작하는 요소가 출력된다.
버블링을 멈추려면 이벤트 객체에 e.stopPropagation();을 사용하면 된다.
꼭 필요한 경우가 아니라면 사용을 피하는 것이 좋다
이벤트 흐름 3가지
캡처링 단계: 이벤트가 하위 요소로 전파되는 단계 (이벤트 객체의 target 프로퍼티가 되는 요소에 등록되어있던 이벤트 핸들러가 동작하는 단계인데, 쉽게 생각해서 가장 처음 이벤트 핸들러가 동작하게 되는 순간이라고 생각하시면 됩니다.)
타깃 단계: 이벤트가 실제 타깃 요소에 전달되는 단계 (이벤트 객체의 target 프로퍼티가 되는 요소에 등록되어있던 이벤트 핸들러가 동작하는 단계인데, 쉽게 생각해서 가장 처음 이벤트 핸들러가 동작하게 되는 순간이라고 생각하시면 됩니다.)
버블링 단계: 이벤트가 상위 요소로 전파되는 단계
이벤트 위임 (Event Delegation)
const List = document.quertySelelctor('#list');
for(let item of list.children){
item.addEventListener('click', function(e){
e.target.classList.toggle('done');
});
}
이렇게 할 경우 새로운 것을 추가했을 때 거기에는 이벤트가 적용되지 않는다.
list.addEventListener('click', function(e){
e.target.classList.toggle('done');
});
이렇게 부모에게 이벤트를 주게 되면 새로운 것을 추가했을 때에도 적용된다.
그치만 이렇게 하면 자식요소들이 아닌 부모를 클릭해도 이벤트가 적용된다.
따라서 아래처럼 작성해 줘야 한다.
list.addEventListener('click', function(e){
//if(e.target.tagName === 'LI')
if(e.target.classList.contains('item')){
e.target.classList.toggle('done');
});
브라우저 기본 동작
const link = document.querySelecor('#link');
const checkbox = document.querySelecor('#checkbox');
const input = document.querySelecor('#input');
const text = document.querySelecor('#text');
event.preventDefault(이 기능도 꼭 필요한 경우에만 사용해야 한다.)
link.addEventListener('click', function(e){
e.preventDefault();
alert('지금은 이동할 수 없습니다.');
});
input.addEventListener('keydown', function(e){
if(!checkbox.checked){
e.preventDefault();
alert('체크박스를 먼저 클릭해 주세요.');
}
});
text.addEventListener('contextmenu', function(e){
e.preventDefault();
alert('마우스 오른쪽 클릭은 사용할 수 없습니다.');
});
다양한 이벤트
마우스 이벤트 (MouseEvent.type) / 각 이벤트의 순서가 있다
mousedown 마우스 버튼을 누르는 순간
mouseup 마우스 버튼을 눌렀다 떼는 순간
click 왼쪽 버튼을 클릭한 순간
dblclick 왼쪽 버튼을 빠르게 두 번 클릭한 순간
contextmenu 오른쪽 버튼을 클릭한 순간
mousemove 마우스를 움직이는 순간
mouseover 마우스 포인터가 요소 위로 올라온 순간
mouseout 마우스 포인터가 요소에서 벗어나는 순간
mouseenter 마우스 포인터가 요소 위로 올라온 순간 (버블링이 일어나지 않음)
mouseleave 마우스 포인터가 요소에서 벗어나는 순간 (버블링이 일어나지 않음)
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})`);
}
box1.addEventListener('mousemove', onMouseMove);
const box2 = document.querySelector('#box2');
function printEventData(e){
if(e.target.classList.contains('cell')){
e.target.classList.toggle('on');
}
}
box2.addEventListener('mouseover', printEventData);
box2.addEventListener('mouseout', printEventData);
MouseEvent.target: 이벤트가 발생한 요소
MouseEvent.relatedTarget: 이벤트가 발생하기 직전(또는 직후)에 마우스가 위치해 있던 요소
const box2 = document.querySelector('#box2');
function printEventData(e){
console.log('event:', e.type);
console.log('target:', e.target);
console.log('relatedTarget:', e.relatedTarget);
}
box2.addEventListener('mouseover', printEventData);
box2.addEventListener('mouseout', printEventData);
이렇게 하면 마우스 위치가 어디에서 어디로 옮겨졌는지 알 수 있다.
키보드 이벤트
(알파벳, 숫자, 띄어쓰기 등의 출력값이 변하는 키에서만 이벤트가 발생한다. esc나 shift 등의 기능적인 역할을 하는 키에서는 이벤트가 발생하지 않는다.)
(하나의 키를 계속 누르고 있는 경우에도 처음 한번만 이벤트가 발생한다.)
(한글에서는 이벤트가 발생하지 않는다.)
input 태그 다루기