부스트코스 강의를 듣고 정리한 내용.
DOM API 활용
DOM Node 조작하기
다양한 APIs
document.~~
로 사용할 수 있는 API: HTML DOM Document Objectelement.~~
로 사용할 수 있는 API: The HTML DOM Element Object
DOM Element Object
몇 가지 유용한 DOM 엘리먼트 속성
- tagName: 엘리먼트의 태그 이름 리턴
- textContent: 노드의 텍스트 내용을 설정하거나 리턴
- nodeType: 노드의 노드 유형을 숫자로 리턴
DOM 탐색 속성
childNodes
: 엘리먼트의 자식 노드의 노드 리스트 리턴 (텍스트 노드, 주석 노드 포함)firstChild
: 엘리먼트의 자식 엘리먼트 리턴firstElementChild
: 첫 번째 자식 엘리먼트를 리턴. 주석이나 공백을 찾는 일은 거의 없으므로firstChild
보다 많이 쓰게 됨parentElement
: 엘리먼트의 부모 엘리먼트 리턴nextSibling
: 동일한 노드 트리 레벨에서 다음 노드 리턴nextElementSibling
: 동일한 노드 트리 레벨에서 다음 엘리먼트 리턴
DOM 조작 API
삭제, 추가, 이동 , 교체 등의 작업
removeChild()
: 엘리먼트의 자식 노드 제거appendChild()
: 마지막 자식 노드로 자식 노드를 엘리먼트에 추가insertBefore()
: 기존 자식 노드 앞에 새 자식 노드를 추가cloneNode()
: 노드를 복제replaceChild()
: 엘리먼트의 자식 노드 바꿈closest()
: 상위로 올라가면서 가장 가까운 엘리먼트 리턴
HTML을 문자열로 처리해주는 DOM 속성/메소드
innerText
: 지정된 노드와 모든 자손의 텍스트 내용을 설정하거나 리턴innerHTML
: 지정된 엘리먼트의 내부 html을 설정하거나 리턴insertAdjacentHTML
: HTML로 텍스트를 지정된 위치에 삽입
실습
실습 1
strawberry 아래에 새 과일 추가하기
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<h1>selector test</h1>
<ul>
<li>apple</li>
<li>orange</li>
<li>banana</li>
<li>grape</li>
<li>strawberry</li>
</ul>
</body>
</html>
var mango = document.createElement("li");
var mangoText = document.createTextNode("망고");
mango.appendChild(mangoText);
var parent = document.querySelector("ul");
parent.appendChild(mango);
실습 2
insertBefore()
메소드를 사용해 orange와 banana 사이에 새 과일 추가하기
<li>banana</li>
에 class나 id가 없어서 nth-child
를 사용했다. semantic하지 않기 때문에 좋은 코드는 아님.
var peach = document.createElement("li");
var peachText = document.createTextNode("peach");
peach.appendChild(peachText);
var parent = document.querySelector("ul");
var banana = document.querySelector("li:nth-child(3)");
parent.insertBefore(peach, banana);
실습 3
실습 2를 insertAdjacentHTML()
메소드를 사용해 orange와 banana 사이에 새 과일 추가하기
var orange = document.querySelector("li:nth-child(2)");
orange.insertAdjacentHTML("afterend", "<li>fig</li>");
오렌지 뒤에 넣는 대신 바나나 앞에 넣으려면
var banana = document.querySelector("li:nth-child(3)");
banana.insertAdjacentHTML("beforeend", "<li>fig</li>");
기준점이 다른 것 뿐이지 결괏값은 같다.
실습 4
apple을 grape 와 strawberry 사이로 옮기기
insertBefore()
를 사용하면 된다. 어떤 element를 찾았을 때 copy하는게 아니라 잘라서 붙여넣기 해준다고 보면 된다. 노드를 옮길 때 유용
var parent = document.querySelector("ul");
var apple = document.querySelector("li:nth-child(1)");
var strawberry = document.querySelector("li:nth-child(5)")
parent.insertBefore(apple, strawberry);
실습 5
class 가 ‘red’인 노드만 삭제하기
자기 자신을 삭제하는
remove()
메소드가 있지만 IE에서는 적용이 안 된다는 호환성 문제가 있다. 대신 호환성 문제가 없는 removeChild()
를 사용하자. 이 경우 parent로 기준점을 잡아줘야 한다.
var reds = document.querySelectorAll(".red");
var parent = document.querySelector("ul");
reds.forEach(el => parent.removeChild(el))
실습 6
section 태그의 자손 중에 blue라는 클래스를 가지고 있는 노드가 있다면, section 태그 하위에 있는 h2 노드를 삭제하기
element.closest()
: 기준 element에서부터 closest()
메소드를 통해 자신부터 부모 요소 단위로 출발하여 각 요소가 지정한 선택자에 만족할 때까지 탐색한다(문서 루트까지 이동). 이 중 가장 가깝게 조건에 만족한 부모 요소가 반환되며, 조건에 만족한 요소가 없으면 null
값을 반환한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<h1>selector test</h1>
<section>
<h2> red section </h2>
<ul>
<li class="red">apple</li>
<li class="red">orange</li>
<li>banana</li>
<li>grape</li>
<li>strawberry</li>
</ul>
</section>
<Br>
<section>
<h2> blue section </h2>
<ul>
<li class="green blue">apple</li>
<li class="red">orange</li>
<li>banana</li>
<li>grape</li>
<li>strawberry</li>
</ul>
</section>
</body>
</html>
var blues = document.querySelectorAll("section .blue");
blues.forEach(el => {
var section = el.closest("section"); // 조건을 만족하는 가장 가까운 부모 노드를 찾아줌
var h2 = section.querySelector("h2");
section.removeChild(h2);
})
근데 엄밀히 말하자면 blues는 노드 리스트라 배열이 아니다. 배열처럼 리스트 타입이긴 하지만 배열은 아님. 최근에 Array.from()
이라는 메소드가 추가됐는데, IE에서 지원 안 되니까 polyfill 코드를 작성해주면 된다. polyfill이란 개발자가 특정 기능이 지원되지 않는 브라우저를 위해 사용할 수 있는 코드 조각이나 플러그인을 말한다. HTML5 및 CSS3와 오래된 브라우저 사이의 간격을 메꾸는 역할 담당
AJAX
AJAX 응답 처리와 비동기
AJAX란 새로고침이 일어나지 않고 브라우저의 화면 전환 없이 서버 측의 데이터를 별도로 요청해 받아와서 화면에 그려주는 기술을 말한다. 쭉 화면이 그려지지만 만약에 비동기적인 작업, AJAX와 같은 요청이 있으면 그건 보내놓고 나머지 작업을 먼저 하는 것!
AJAX와 비동기
function ajax() {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", function() {
console.log(this.responseText);
});
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();
}
4라인의 익명함수는 8라인과 9라일보다 더 늦게 실행된다. 이 익명함수는 비동기로 실행되며, 이벤트큐에 보관되다가 load 이벤트가 발생하면 (브라우저가 서버로부터 데이터를 받으면) 그때 call stack에 실행되고 있는 함수가 없어서 비어있을 경우 stack에 올라와 실행된다.
동기/비동기에 대한 것은 아래 자료 참고
아래 그림은 AJAX 통신(제이쿼리 라이브러리를 사용한 예제)을 코드 단위로 어떻게 비동기로 처리되는지 보여줌
AJAX 응답 문제
서버로부터 받아온 JSON 데이터를 문자열 형태이므로 브라우저에서 바로 실행할 수 없다. 문자열을 JS 객체로 변환해야 데이터에 접근할 수 있는데, 이를 위해 문자열을 일일이 파싱해야 하는 불편함이 있다.
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", function() {
console.log(this.responseText);
});
oReq.open("GET", "./json.txt");
oReq.send();
브라우저에서 제공하는 JSON객체를 활용하면 자바스크립트 객체로 변환할 수 있다.
var json객체로변환된값 = JSON.parse("서버에서 받은 JSON 문자열");
cross domain 문제
XHR 통신은 다른 도메인 간에는 보안을 이유로 요청이 안 된다. 즉 A 도메인에서 B도메인으로 XHR 통신, AJAX 통신을 할 수 없다. 이를 피하기 위해 JSONP라는 방식이 널리 사용되고 있고, 최근에는 CORS라는 표준적인 방법이 제공되고 있어 이를 활용하는 경우도 등장했다.
CORS를 사용하기 위해서는 프로그램 코드에서 별도로 해야할 것은 없지만 백엔드 코드에서 헤더 설정을 해야 하는 번거로움이 있다. JSONP는 비표준이지만 아직도 많은 곳에서 사용하는 사실상 표준. CORS로 가기 전에 많은 곳에서 사용한다.
Web Animation
사용자들에게 매끄러운 UX를 제공해줘야 하기 때문에 얼만큼 부드럽게 이동할 수 있는가, 어떻게 끊김 없이 부드럽게 이어질 수 있을까 등이 관건.
웹 애니메이션의 이해와 setTimeout 활용
애니메이션
반복적인 움직임의 처리. 웹 UI 애니메이션은 JS로 다양하게 제어할 수 있지만, 규칙적이고 비교적 단순하게 동작되는 방식은 CSS3의 transition과 transform 속성을 사용해 대부분 구현할 수 있다. 심지어는 JS보다 더 빠른 성능을 보장하는데, 모바일 웹에서는 CSS를 사용한 방식이 훨씬 더 빠르다.
FPS (Frame Per Second)
1초동안 화면에 표현할 수 있는 정지화면(frame)의 수. 매끄러운 애니메이션은 보통 16fps이므로 1000ms/60fps =16.666ms 간격으로 frame 생성이 필요하다.
애니메이션은 CSS의 transition 속성이므로 CSS 속성을 변경하거나 JS로 CSS 속성을 변경할 수 있다.
정리하자면,
- 간단하고 규칙적인 것: CSS로 변경
- 세밀한 조작이 필요한 것: JS로 변경
Javascript 애니메이션
JS로 애니메이션을 구현하려면 규칙적인 처리를 하도록 구현하면 된다. setInterval
, setTimeout
, requestAnimationframe
, AnimationsAPI
와 같은 방법들이 있음.
setInterval()
const interval = window.setInterval(()=> {
console.log('현재시각은', new Date());
},1000/60);
window.clearInterval(interval);
주어진 시간에 따라 함수 실행이 가능하지만, 정해진 시간에 함수가 실행된다고 보장할 수 없다. 아래 그림에서도 제때 일어나야 할 이벤트 콜백이 지연되거나 없어지는 것을 볼 수 있다. 그래서 일반적으로는 setInterval을 사용하는 애니메이션은 잘 구현하지 않는다.
setTimeout()
setTimeout(() => {
console.log('현재시각은', new Date());
},500);
애니메이션을 구현하려면 재귀호출을 해서 구현할 수 있다.
let count = 0;
function animate() {
setTimeout(() => {
if(count >= 20) return;
console.log('현재시각은', new Date());
count++;
animate();
},500);
}
animate();
setTimeout도 엄밀하게는 어떤 이유로 인해 제때에 원하는 콜백함수가 실행되지 않을 수 있다. 그렇지만 setTimeout은 매 순간 timeout을 조절할 수 있는 코드구현으로 컨트롤 가능한 부분이 있다는 점이 setInterval과 다르다.
requestAnimationFrame 활용
setTimeout은 애니메이션을 위한 최적화된 기능이라 보기 어렵다. 애니메이션 주기를 16.6 미만으로 하는 경우 불필요한 프레임이 생성되는 등의 문제가 발생한다. 그래서 대안으로 생긴 것이 requestAnimationFrame이다.
아래 코드를 보자.
var count = 0;
var el=document.querySelector(".outside");
el.style.left = "0px";
function run() {
if(count > 70) return;
count = count + 1;
el.style.left = parseInt(el.style.left) + count + "px";
requestAnimationFrame(run);
}
requestAnimationFrame(run);
먼저 requestAnimationFrame을 한 번 실행시켜준 다음에 특정 조건(함수의 탈출 조건)에 도달할 때까지 함수를 계속 연속적으로 호출한다. 이렇게 requestAnimationFrame으 통해 원하는 함수를 인자로 넣어주면, 브라우저는 인자로 받은 그 비동기 함수가 실행될 가장 적절한 타이밍에 실행시킨다. 브라우저를 믿고 함수를 전달하는 것이다.
canvas나 svg를 사용하면서 그래픽 작업을 하게 될 때 복잡한 애니메이션을 다룰 필요가 생기는데, 이렇게 자바스크립트로 복잡한 애니메이션처리를 처리해야 할 때 requestAnimationFrame은 꽤 유용하게 쓰일 수 있다.
CSS3 transition 활용
CSS 기법으로 애니메이션 구현
transition을 이요한다. 이 방법이 JS로 구현하는 것보다 더 빨라 모바일 웹에서는 transform을 사용한 element의 조작을 많이 구현한다.
CSS Transitions and Transforms for Beginners
더 빠른 CSS3 애니메이션 관련 속성들
GPU 가속을 이용하는 속성을 사용하면 애니메이션을 더 빠르게 처리할 수 있다.
- transform : translateXX();
- transform : scale();
- transform : rotate();
- opacity