[코드잇] 인터랙티브 자바스크립트 ① 시작하기, 브라우저와 자바스크립트
1. 인터랙티브 자바스크립트 시작하기
id로 태그 선택하기
document.getElementsById('id')
→ id에 해당하는 태그 하나
const myTag = document.getElementById('id');
존재하지 않는 id 넣으면 null이 리턴됨.
const myTag2 = document.getElementById('no_id');
consoel.log(myTag2) // null
class로 태그 선택하기
document.getElementsByClassName('class')
→ class에 해당하는 태그 모음(HTMLCollection)
유사배열 형태로 출력됨.
배열의 형태이지만 완벽한 배열은 아님.
· splice, push같은 배열의 메소드 사용 X
· 대괄호 표기로 인덱스 접근 O
· length 프로퍼티 사용 O
· for of문으로 반복 O
const myTags1 = document.getElementsByClassName('color-btn');
console.log(myTags1);
console.log(myTags1[1]);
console.log(myTags1.length); // 8
for (let tag of myTags1) {
console.log(tag);
}
// 하나밖에 없는 클래스
// → 똑같이 유사배열 형태, 인덱스로 접근해야 됨
const myTags2 = document.getElementsByClassName('red');
console.log(myTags2); // HTMLCollection(Int16Array)
console.log(myTags2[0]);
// 존재하지 않는 클래스
// → null (X), 빈 HTMLCollection 출력
const myTags3 = document.getElementsByClassName('white');
console.log(myTags3); // HTMLCollection(0)
console.log(myTags3 === null); // false
console.log(myTags3.length); // 0
유사 배열(Array-Like Object)
유사배열의 종류가 다양해서 아래 특징이 반드시 적용되는 것은 아님.
- length 프로퍼티가 있음
- 배열의 기본 메소드를 사용할 수 없음
- Array.isArray(유사배열)은 false
태그 이름으로 태그 선택하기
document.getElementsByTagName('태그이름') 메소드
→ tag에 해당하는 태그 모음(HTMLCollection)
const btns = document.getElementsByTagName('button');
const allTags = document.getElementsByTagName('*');
이렇게 한꺼번에 많은 요소들을 다루면 실수할 가능성이 높아지기 때문에 많이 사용되는 메소드는 아님.
css 선택자로 태그 선택하기
• 하나의 요소 : querySelector
여러 개의 요소가 존재하는 선택자를 쓰더라도 그 중 가장 첫번째 요소 하나만 선택
→ 존재하지 않는 요소를 선택하는 경우 null값을 리턴함
const myTag = document.querySelector('#myNumber'); // 아이디 이름
console.log(myTag);
// <div id="myNumber">0</div>
const myTag = document.querySelector('.color-btn'); // 클래스 이름
console.log(myTag);
// <button class="color-btn red" data-color="#FF0000"></button>
// color-btn 클래스가 붙어있는 태그들 중에서 가장 첫번째 요소가 선택됨.
• 여러 개의 요소 : querySelectorAll
NodeList라는 이름의 유사 배열에 각 요소가 담김
→ 존재하지 않는 요소를 선택하는 경우 undefined나 null값이 리턴되는 것이 아니라 비어있는 NodeList가 리턴됨.
const myTags = document.querySelectorAll('.color-btn');
console.log(myTags);
HTML class 속성으로 태그 선택
const myTags2 = document.getElementsByClassName('color-btn');
console.log(myTags2);
이벤트와 버튼 클릭
• 이벤트 : 웹 페이지에서 발생하는 대부분의 일들 (클릭, 스크롤 등)
• 이벤트 핸들링 : 이벤트가 발생했을 때 어떤 특별한 동작을 하도록 다루는 것
• 이벤트 핸들러 : 구체적인 동작들을 코드로 표현한 함수 부분 ( = 이벤트 리스너(Event Listener))
자바스크립트로 해당 DOM 객체의 onclick 프로퍼티에 등록하기
// 이벤트(Event)와 버튼 클릭
const btn = document.querySelector('#myBtn');
// 이벤트 핸들링(Event Handling)
btn.onclick = function() { // 이벤트 핸들러(Event Handler)
console.log('Hello World!');
};
html의 <body>에 onclick 속성으로 바로 표시하기
<body>
<div>
<button id="myBtn" onclick="console.log('Hello World!')">Click!</button>
</div>
<script src="index.js"></script>
</body>
해당 방법은 잘 사용되는 방법이 아님.
- HTML 파일 가독성 떨어짐
- HTML과 Javascript 코드가 섞여서 유지 보수에 큰 어려움이 생김.
2. 브라우저와 자바스크립트
윈도우 객체 (전역 객체, global object)
브라우저의 창을 대변함.
자바스크립트의 최상단에 위치한 객체임.
윈도우 객체가 다른 객체를 포함하고 있음.
자바스크립트의 어느 곳에서나 접근할 수 있음.
자바스크립트로 브라우저가 가지고 있는 다양한 정보들을 얻거나 혹은 브라우저를 자유롭게 제어할 수 있음.
→ 어떤 프로퍼티나 메소드든 결국 전역 객체 내부의 것이므로 앞에 'window.'을 생략
// window: 전역객체 Global Object
console.log(window);
console.log(window.innerWidth); // 탭 내부 너비 1004
console.log(window.innerHeight); // 탭 내부 높이 744
window.open() // 새로운 창이 열림
window.close() // close 메소드를 호출한 창이 닫힘
DOM
DOM(Document Object Model) 문서 객체 모델
· 웹페이지에 나타나는 HTML 문서 전체를 객체로 표현한 것
· document 객체가 이 웹문서의 최상단 객체로 진입점의 역할을 함.
· 각 객체 = 노드(Node), 태그는 요소노드, 문자는 텍스트 노드로 구분됨.
console.log(document);
console.log(typeof document); // object
console.log(document)처럼 document 객체를 직접 출력하면 DOM에 해당하는 HTML이 출력됨.
console.dir(document);
dom을 태그가 아니라 객체로 보고 싶으면 console.log가 아니라 콘솔 객체의 dir 메소드를 활용하면 됨.
const title = document.querySelector('#title');
console.dir과 console.log
console.dir | console.log | |
출력하는 자료형 | 원래 값의 자료형 그대로 | 문자열 형식 |
출력하는 내용 | 파라미터로 전달받은 값을 위주로 출력 | 객체의 속성을 좀 더 자세히 출력 |
파라미터로 전달하는 값의 개수 | 여러값 전달 시 모든 값을 출력 | 여러값 전달하더라도 첫 번째 값만 출력 |
DOM 객체 | HTML 형태로 출력 | 객체 형태로 출력 |
DOM 트리
• 요소 노드 (Element Node): HTML 또는 XML 문서에서 태그를 나타내는 노드
이 노드들은 문서의 구조를 형성하며, 각 요소 노드는 특정한 태그를 나타냄.
• 텍스트 노드 (Text Node): 문자열을 나타내는 노드로, 주로 요소 노드의 내용을 포함함.
- 일반적으로 요소 노드의 자식노드가 됨.
- 스스로는 자식 노드를 가질 수 없음.
• 코멘트 노드 (Comment Node): 주석을 나타내는 노드로 <!-- --> 표현됨.
• 문서 노드 (Document Node): 문서 전체를 나타내는 노드입니다.
- HTML 문서의 최상위 노드로, 모든 요소, 텍스트, 주석 등이 이 노드 아래에 포함됨.
- HTML 문서의 시작 부분에는 과 같은 선언문이 있고, 그 아래에 문서 노드가 시작됨.
노드 유형별 프로퍼티
// DOM 트리 여행하기
const myTag = document.querySelector('#list-1');
console.log(myTag);
// 형제 요소 노드
console.log(myTag.previousElementSibling);
console.log(myTag.nextElementSibling);
// 부모 요소 노드
console.log(myTag.parentElement);
프로퍼티 | 유형 | 결과 |
element.children | 자식 요소 노드 | element의 자식 요소 모음(HTMLCollection) |
element.firstElementChild | 자식 요소 노드 | element의 첫 번째 자식 요소 하나 |
element.lastElementChild | 자식 요소 노드 | element의 마지막 자식 요소 하나 |
element.parentElement | 부모 요소 노드 | element의 부모 요소 하나 |
element.previousElementSibling | 형제 요소 노드 | element의 이전(previous) 혹은 좌측(left)에 있는 요소 하나 |
element.nextElementSibling | 형제 요소 노드 | element의 다음(next) 혹은 우측(right)에 있는 요소 하나 |
요소 노드 프로퍼티
innerHTML
· 해당 요소의 HTML 콘텐츠를 가져옴. 줄바꿈, 띄어쓰기 포함됨.
· innerHTML은 요소 안 HTML을 확인하는 것보다 수정하는 데 더 활용됨.
<div id="myDiv">
<p>Hello, <strong>world!</strong></p>
</div>
<script>
const myDiv = document.getElementById('myDiv');
console.log(myDiv.innerHTML);
// 출력: "<p>Hello, <strong>world!</strong></p>"
</script>
outerHTML
· 해당 요소를 포함한 전체 HTML 코드를 문자열로 리턴해줌. 줄바꿈, 띄어쓰기 포함됨.
· 해당 요소 자체를 가리키는 특성 때문에 요소 자체가 새로운 요소로 교체됨.
· outerHTML 에 값을 할당하게 되면 처음 선택한 요소는 사라짐.
<div id="myDiv">
<p>Hello, <strong>world!</strong></p>
</div>
<script>
const myDiv = document.getElementById('myDiv');
console.log(myDiv.outerHTML);
// 출력: "<div id="myDiv"><p>Hello, <strong>world!</strong></p></div>"
</script>
textContent
· 요소 안에 있는 내용 중에서 HTML을 제외한 텍스트를 가져옴.
· 텍스트만 다르기 때문에 특수문자도 텍스트로 처리함.
→ 원치 않은 HTML코드가 추가되는 걸 방지할 수 있음.
<div id="myDiv">
<p>Hello, <strong>world!</strong></p>
</div>
<script>
const myDiv = document.getElementById('myDiv');
console.log(myDiv.textContent);
// 출력: "Hello, world!"
</script>
요소 노드 추가하기
// 현재 날짜로 선택된 요소에 내용 추가하기
const today = document.querySelector('#today');
// 현재 날짜의 내용 앞에 새로운 <li> 요소 추가 (맨 처음에)
today.innerHTML = '<li>처음</li>' + today.innerHTML;
// 현재 날짜의 내용 뒤에 새로운 <li> 요소 추가 (맨 마지막에)
today.innerHTML = today.innerHTML + '<li>마지막</li>';
// 현재 날짜 요소 자체를 변경하여 외부에 <p> 요소 추가 (이전에)
today.outerHTML = '<p>이전</p>' + today.outerHTML;
// 변경된 DOM에서 다시 현재 날짜로 선택된 요소 가져오기
const newToday = document.querySelector('#today');
// 현재 날짜의 내용 뒤에 <p> 요소 추가 (맨 뒤에)
newToday.outerHTML = newToday.outerHTML + '<p>다음</p>';
// 변경된 DOM에서 다시 현재 날짜로 선택된 요소 가져오기
const newToday = document.querySelector('#today');
// 현재 날짜의 내용 뒤에 <p> 요소 추가 (맨 뒤에)
newToday.outerHTML = newToday.outerHTML + '<p>다음</p>';
// 새로운 날짜로 선택된 요소에 요소 노드 추가하기
const tomorrow = document.querySelector('#tomorrow');
// 새로운 <li> 요소 만들기
const first = document.createElement('li');
first.textContent = '처음';
// 첫 번째 자식으로 추가하기
tomorrow.prepend(first);
// 새로운 <li> 요소 만들기
const last = document.createElement('li');
last.textContent = '마지막';
// 마지막 자식으로 추가하기
tomorrow.append(last);
// 새로운 <p> 요소 만들기
const prev = document.createElement('p');
prev.textContent = '이전';
// 현재 요소의 이전 형제로 추가하기
tomorrow.before(prev);
// 새로운 <p> 요소 만들기
const next = document.createElement('p');
next.textContent = '다음';
// 현재 요소의 다음 형제로 추가하기
tomorrow.after(next);
노드 삭제와 이동하기
• 요소 노드 만들기 : document.createElement('태그이름')
• 요소 노드 꾸미기 : element.textContent, element.innerHTML, ...
• 요소 노드 추가 혹은 이동하기 :
- 부모 요소의 자식 노드 중 맨 앞에 추가 : element.prepend
- 부모 요소의 자식 노드 중 맨 뒤에 추가 : element.append
- 현재 요소의 뒤에 추가 : element.after
- 현재 요소의 앞에 추가 : element.before
• 요소 노드 삭제 : element.remove()
// 노드 삭제와 이동
const today = document.querySelector('#today');
const tomorrow = document.querySelector('#tomorrow');
// 노드 삭제하기: Node.remove()
tomorrow.remove();
today.children[2].remove(); // today의 세 번째 자식 요소
// 노드 이동하기: 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 속성 다루기
대부분의 HTML 속성들은 이름 그대로 요소 노드의 프로퍼티로 생성이 됨.
모든 HTML 속성이 요소 노드의 프로퍼티로 생성되는 것은 아님.
비표준속성의 예시
<ol id="tomorrow" href="https://www.codeit.kr">
console.log(tomorrow.href); // undefined
ol 태그에 href 속성을 추가하는 건 HTML 표준이 아님.
HTML 태그의 클래스 속성은 className이라는 이름으로 프로퍼티가 생성됨.
클래스 속성은 이름이 살짝 다름.
console.log(item.className); // item
그럼 HTML 표준이 아닌 속성은 접근 불가인가? 아님.
프로퍼티에 직접 접근하는 방식으로는 비표준 속성에는 접근할 수 없지만 getAttribute, setAttribute, removeAttribute 메소드를 활용하면 표준 여부와 관계없이 HTML 문서에서 해당 태그에 추가된 모든 속성들에 접근할 수 있음.
getAttribute() 안에 오는 것들은 속성 이름 모두 대소문자를 구분하지 않음.
HTML 표준 속성은 모두 소문자이기 때문에 만약 속성 이름에 대문자를 섞어 사용하더라도 소문자로 변환돼서 동작함.
• 속성에 접근하기 : element.getAttribute('속성')
• 속성 추가(수정)하기 : element.setAttribute('속성', '값')
• 속성 제거하기 : 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'));
// elem.setAttribute('속성', '값'): 속성 추가(수정)하기
tomorrow.setAttribute('class', 'list'); // 추가
link.setAttribute('href', 'https://www.codeit.kr'); // 수정
link.setAttribute('href', undefined); // 삭제 X, undefined값으로 수정 O
// elem.removeAttribute('속성'): 속성 제거하기
tomorrow.removeAttribute('href');
tomorrow.removeAttribute('class');
참고링크 : https://html.spec.whatwg.org/#attributes-3
스타일 다루기
Javascript로 HTML 요소에 스타일을 적용하는 방식은 크게 2개가 있음.
• style 프로퍼티 활용하기 : element.style.styleName = 'value';
• class 변경을 통해 간접적으로 스타일을 적용하기 : element.className, element.classList
style 프로퍼티 활용
// 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';
· 여러 단어를 하이픈(-)으로 이어서 만든 속성(styleName)은 무조건 카멜표기법
ex. backgroud-color → backgroudColor, font-size → fontSize
main.style.boxSizing = 'border-box';
· 의도된 상황이 아니라면 일반적으로 JavaScript를 활용해서 스타일을 입힐 때 style 프로퍼티를 사용하는 것보다 태그의 클래스를 변경하는 방식이 좀 더 권장됨.
element.className
.done {
opacity: 0.5;
text-decoration: line-through;
}
// elem.className
today.children[1].className = 'done';
done 클래스에 미리 작성해둔 스타일이 입혀지는 방식으로 동작함.
element.classList
· 클래스 리스트 프로퍼티 활용
· 클래스의 속성값을 유사배열로 다룸.
· add, remove, toggle 메소드로 추가/삭제/추가 혹은 삭제 가능함.
// elem.classList
// 스타일 다루기
const today = document.querySelector('#today');
const tomorrow = document.querySelector('#tomorrow');
// elem.classList: add, remove, toggle
const item = tomorrow.children[1];
// add
itehttp://m.classList.add('done');
//중복으로 추가해도 결국 하나만 추가됨
itehttp://m.classList.add('done', 'other');
itehttp://m.classList.add('done', 'other');
// remove
itehttp://m.classList.remove('done');
// toggle : 클래스 없으면 추가, 있으면 삭제
itehttp://m.classList.toggle('done') // 위 설명대로 동작
itehttp://m.classList.toggle('done', true); // 추가
itehttp://m.classList.toggle('done', false); // 삭제
비표준 속성 다루기
html의 비표준 속성 활용 용도
• 선택자로 활용
• 객체 형태의 데이터에서 값을 표시할 태그를 구분할 때 사용
• 스타일이나 데이터 변경에 활용
선택자로 활용
비표준 속성을 사용하여 특정 요소를 선택
<div selector-type="custom-selector">
<p>This is a content inside the div.</p>
</div>
[selector-type="custom-selector"] {
background-color: lightblue;
}
값을 표시할 태그를 구분할 때 사용
객체 형태의 데이터가 있을 때, 각 프로퍼티 값들이 들어갈 태그를 구분하는데 활용
<span field="title"></span>
<span field="manager"></span>
<span field="status"></span>
// HTML 문서에서 [field] 속성을 가진 모든 요소를 선택
const fields = document.querySelectorAll('[field]');
const task = {
title: '코드 에디터 개발',
manager: 'CastleRing, Raccoon Lee',
status: '',
};
for (let tag of fields) {
// 각 요소의 field 속성 값을 가져옴
const field = tag.getAttribute('field');
// 해당 요소의 텍스트 내용을 작업 객체의 속성 값으로 설정
tag.textContent = task[field];
}
스타일이나 데이터 변경에 활용
getAttribute 메소드를 활용해서 속성값을 가져오고, setAttribute 메소드를 활용해서 속성값을 설정해주는 원리
<button class="btn" status="In Progress">Start</button>
<button class="btn" status="Completed">Finish</button>
// ...... 위 내용에 이어서
const btns = document.querySelectorAll('.btn');
for (let btn of btns) {
const status = btn.getAttribute('status');
btn.onclick = function () {
fields[2].textContent = status;
fields[2].setAttribute('status', status);
};
}
[field] 속성을 가진 모든 요소를 선택하고, task 객체의 속성 값을 해당 요소에 할당한 후, .btn 클래스를 가진 버튼 요소들에 대해 클릭 이벤트를 처리하는 역할
비표준 속성 사용 방식 : data-* 속성
• data-로 시작하는 속성은 모두 dataset이라는 프로퍼티에 저장됨.
• data-status라는 속성이 있다면, element.dataset.status라는 프로퍼티에 접근해서 그 값을 가져올 수 있음.
위 예제코드에서 속성 이름 앞에 'data-'를 붙여주면 됨.
<p>할 일 : <b data-field="title"></b></p>
<p>담당자 : <b data-field="manager"></b></p>
<p>상태 : <b data-field="status"></b></p>
<button class="btn" data-status="대기중">대기중</button>
<button class="btn" data-status="진행중">진행중</button>
<button class="btn" data-status="완료">완료</button>
CSS에서 대괄호를 사용하는 속성 선택자에서 속성 이름 앞에 모두 'data-'를 붙임.
/* CSS */
[data-status] {
padding: 5px 10px;
}
자바스크립트 코드에서도 속성 이름에 'data-'를 붙임.
const fields = document.querySelectorAll('[data-field]');
for (let tag of fields) {
const field = tag.dataset.field;
tag.textContent = task[field];
}
const status = btn.dataset.status;
btn.onclick = function () {
fields[2].textContent = status;
fields[2].dataset.status = status;