- JavaScript가 브라우저와 상호 작용하도록 허용합니다.
- JavaScript를 작성하여 HTML 요소를 생성, 수정 및 삭제할 수 있습니다.
스타일, 클래스 및 속성을 설정합니다. 이벤트를 듣고 응답합니다.
- DOM 트리는 HTML 문서에서 생성됩니다. 상호 작용하다;
- DOM은 많은 메소드와 DOM 트리와 상호 작용하는 속성 (API)
.querySelector() / .addEventListener() / .createElement() / .innerHTML / .textContent / .children / etc ...
console.log(document.documentElement); // html
console.log(document.head); // head
console.log(document.body); // body
const header = document.querySelector(`.header`);
const allSection = document.querySelectorAll(`.section`);
console.log(allSection); // NodeList(4) [section#section--1.section, section#section--2.section, section#section--3.section, section.section.section--sign-up]
document.getElementById(`section--1`);
const allButtons = document.getElementsByTagName(`button`); // 태그의 button이 들어간 모든 개체를 불러옴
console.log(
allButtons
); /* HTMLCollection(9) [button.btn--text.btn--scroll-to, button.btn.operations__tab.operations__tab--1.operations__tab--active,
button.btn.operations__tab.operations__tab--2, button.btn.operations__tab.operations__tab--3, button.slider__btn.slider__btn--left, button.slider__btn.slider__btn--right,
button.btn.btn--show-modal, button.btn--close-modal, button.btn]*/
console.log(
document.getElementsByClassName(`btn`) // 클래스의 btn이 들어간 모든 개체를 불러옴
); /*HTMLCollection(5) [button.btn.operations__tab.operations__tab--1.operations__tab--active,
button.btn.operations__tab.operations__tab--2, button.btn.operations__tab.operations__tab--3, button.btn.btn--show-modal, button.btn] */
// Creating and inserting elements
const message = document.createElement(`div`);
message.classList.add(`cookie-message`);
// message.textContent = `We use cookied for improved functionality and anlytics.`
message.innerHTML = `We use cookied for improved functionality and anlytics. <button class="btn btn--close--cookie">Got it!</button>`;
// header.prepend(message);
header.append(message); // (prepend 와 append는 생명요소이기에 한번만 사용 가능하다.)
// header.prepend(message.cloneNode(true)); // (cloneNode를 사용하면 중복사용 가능)
// header.before(message);
// header.after(message);
// Delete elments (요소 삭제)
document
.querySelector(`.btn--close--cookie`)
.addEventListener(`click`, function () {
message.remove();
});
// Styles
message.style.backgroundColor = `#37383d`;
message.style.width = `120%`;
console.log(message.style.height);
console.log(message.style.backgroundColor);
console.log(getComputedStyle(message).color);
console.log(getComputedStyle(message).height);
message.style.height =
Number.parseFloat(getComputedStyle(message).height, 10) + 30 + `px`;
document.documentElement.style.setProperty(`--color-primary`, `orangered`);
// Attributes
const logo = document.querySelector(`.nav__logo`);
console.log(logo.alt); // Bankist logo
console.log(logo.className); // nav__logo
logo.alt = `Beautiful minimalist logo`;
// Non-standard
console.log(logo.designer); // undefined
console.log(logo.getAttribute(`designer`)); // vancouver
logo.setAttribute(`company`, `Bankist`); // (creating Attribute)
console.log(logo.src); // http://127.0.0.1:5500/img/logo.png
console.log(logo.getAttribute(`src`)); // img/logo.png
const link = document.querySelector(`.nav__link--btn`);
console.log(link.href); // http://127.0.0.1:5500/index.html#
console.log(link.getAttribute(`href`)); // # (html의 있는 그대로를 출력)
// Data attributes
console.log(logo.dataset.versionNumber); // 3.0
//classes
logo.classList.add(`c`, `j`);
logo.classList.remove(`c`, `j`);
logo.classList.toggle(`c`, `j`);
logo.classList.contains(`c`, `j`);
// Don't use
logo.className = `vancouver`;
const btnScrollTo = document.querySelector(`.btn--scroll-to`);
const section1 = document.querySelector(`#section--1`);
btnScrollTo.addEventListener(`click`, function (e) {
const s1coords = section1.getBoundingClientRect();
console.log(s1coords);
console.log(e.target.getBoundingClientRect());
console.log(`Current scroll (X/Y)`, window.pageXOffset, window.pageYOffset); // 가시 뷰로 스크롤을 상하좌우 할때 현재 X,Y좌표를 찍어줌
console.log(
`heigth/width viewport`,
document.documentElement.clientHeight,
document.documentElement.clientWidth
); // 페이지 상단에서 버튼 사이의 거리를 측정
// Scrolling
// window.scrollTo(
// s1coords.left + window.pageXOffset,
// s1coords.top + window.pageYOffset
// );
// window.scrollTo({
// left: s1coords.left + window.pageXOffset,
// top: s1coords.top + window.pageYOffset,
// behavior: `smooth`,
// });
section1.scrollIntoView({ behavior: `smooth` });
});
const h1 = document.querySelector(`h1`);
const alertH1 = function (e) {
alert(`addEventListener: Great! You are reading the heading :D`);
// h1.removeEventListener(`mouseenter`, alertH1); (이벤트 리스너를 삭제, 한번만 허용)
};
h1.addEventListener(`mouseenter`, alertH1); // h1의 마우스를 눌렀을때의 이벤트리스너
setTimeout(() => h1.removeEventListener(`mouseenter`, alertH1), 3000);
// 3초가 지난뒤 eventListener를 삭제
// h1.onmouseenter = function (e) {
// alert(`onmouseenter: Great! You are reading the heading :D`);
// };
// rgb(255, 255, 255)
const randomInt = (min, max) =>
Math.floor(Math.random() * (max - min + 1) + min);
const randomColor = () =>
`rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`;
console.log(randomColor(0, 255));
document.querySelector(`.nav__link`).addEventListener(`click`, function (e) {
this.style.backgroundColor = randomColor(); // Features
console.log(`LINK`, e.target);
});
document.querySelector(`.nav__links`).addEventListener(`click`, function (e) {
this.style.backgroundColor = randomColor(); // container (parents element)
console.log(`LINK`, e.target);
});
document.querySelector(`.nav`).addEventListener(`click`, function (e) {
this.style.backgroundColor = randomColor(); // NAV (parents element)
console.log(`LINK`, e.target);
});
// Page Navigation
// document.querySelectorAll(`.nav__link`).forEach(function (el) {
// el.addEventListener(`click`, function (e) {
// e.preventDefault();
// const id = this.getAttribute(`href`);
// console.log(id);
// document.querySelector(id).scrollIntoView({ behavior: `smooth` });
// });
// });
// 1. Add event listener to common parent element
// 2. Determine what element originated the event
document.querySelector(`.nav__links`).addEventListener(`click`, function (e) {
e.preventDefault();
//Matching strategy
if (e.target.classList.contains(`nav__link`)) {
const id = e.target.getAttribute(`href`);
console.log(id);
document.querySelector(id).scrollIntoView({ behavior: `smooth` });
}
});// Features / Operations / Testimonials 버튼을 각각 눌렀을때 부드럽게 창으로 넘어가는 효과 부여
console.log(h1.querySelectorAll(`.highlight`));
console.log(h1.childNodes);
console.log(h1.children);
h1.firstElementChild.style.color = `white`;
h1.lastElementChild.style.color = `orangered`;
// Going upwards: parents
console.log(h1.parentNode);
console.log(h1.parentElement);
h1.closest(`.header`).style.background = `var(--gradient-secondary)`;
h1.closest(`h1`).style.background = `var(--gradient-primary)`;
// Going sideways: siblings
console.log(h1.previousElementSibling);
console.log(h1.nextElementSibling);
console.log(h1.previousSibling);
console.log(h1.nextSibling);
console.log(h1.parentElement.children);
console.log(h1.parentElement.children);
[...h1.parentElement.children].forEach(function (el) {
if (el !== h1) el.style.transform = `scale(0.5)`;
});
// Tabed component
const tabs = document.querySelectorAll(`.operations__tab`);
const tabsContainer = document.querySelector(`.operations__tab-container`);
const tabsContents = document.querySelectorAll(`.operations__content`);
tabsContainer.addEventListener(`click`, function (e) {
const clicked = e.target.closest(`.operations__tab`);
// Guard clasuse
if (!clicked) return; // target 범위밖에 클릭을 했을때 오류를 방지.
// Remove active classes
tabs.forEach((t) => t.classList.remove("operations__tab--active"));
tabsContents.forEach((c) =>
c.classList.remove(`operations__content--active`)
);
// Activate tab
clicked.classList.add(`operations__tab--active`);
// Activate content area
console.log(clicked.dataset.tab);
document
.querySelector(`.operations__content--${clicked.dataset.tab}`)
.classList.add(`operations__content--active`);
});
// Menu fade animation
const handleHover = function (e) {
if (e.target.classList.contains(`nav__link`)) {
const link = e.target;
const siblings = link.closest(`.nav`).querySelectorAll(`.nav__link`);
const logo = link.closest(`.nav`).querySelector(`img`);
siblings.forEach((el) => {
if (el !== link) el.style.opacity = this;
});
logo.style.opacity = this;
}
};
// Passing "argument" into handler
nav.addEventListener(`mouseover`, handleHover.bind(0.5));
nav.addEventListener(`mouseout`, handleHover.bind(1));
// Sticky navigation
const initialCoords = section1.getBoundingClientRect();
window.addEventListener(`scroll`, function () {
if (window.scrollY > initialCoords.top) nav.classList.add(`sticky`);
else nav.classList.remove(`sticky`);
});
// Sticky navigation: Intersection Observer API
const header = document.querySelector(`.header`);
const navHeight = nav.getBoundingClientRect().height;
console.log(navHeight);
const stickyNav = function (entries) {
const [entry] = entries;
console.log(entry);
if (!entry.isIntersecting) nav.classList.add(`sticky`);
else nav.classList.remove(`sticky`);
};
const headerObserver = new IntersectionObserver(stickyNav, {
root: null,
threshold: 0,
rootMargin: `-${navHeight}px`, // navigation의 header margin의 맞춰 동적으로 전환
});
headerObserver.observe(header);
// Reveal sections
const allSections = document.querySelectorAll(`.section`);
const revealSection = function (entries, observer) {
const [entry] = entries;
console.log(entry);
if (!entry.isIntersecting) return;
entry.target.classList.remove(`section--hidden`);
observer.unobserve(entry.target);
};
const sectionObserver = new IntersectionObserver(revealSection, {
root: null,
threshold: 0.15,
});
allSections.forEach(function (section) {
sectionObserver.observe(section);
section.classList.add(`section--hidden`);
});
// Lazy loading images
const imgTargets = document.querySelectorAll(`img[data-src]`);
const loadImg = function (entries, observer) {
const [entry] = entries;
console.log(entry);
if (!entry.isIntersecting) return;
// Replace src with data-src
entry.target.src = entry.target.dataset.src;
entry.target.addEventListener(`load`, function () {
entry.target.classList.remove(`lazy-img`);
});
observer.unobserve(entry.target);
};
const imgObserver = new IntersectionObserver(loadImg, {
root: null,
threshold: 0,
rootMargin: `-200px`,
});
imgTargets.forEach((img) => imgObserver.observe(img));
// EXAMPLE
// const obsCallback = function (entries, observer) {
// entries.forEach((entry) => {
// console.log(entry);
// });
// };
// const obsOptions = {
// root: null,
// threshold: [0, 0.2],
// };
// const observer = new IntersectionObserver(obsCallback, obsOptions);
// observer.observe(section1);
// Slider Component : Part 1 , 2
const slider = function () {
const slides = document.querySelectorAll(`.slide`);
const btnLeft = document.querySelector(`.slider__btn--left`);
const btnRight = document.querySelector(`.slider__btn--right`);
const dotContainer = document.querySelector(`.dots`);
const maxSlide = slides.length;
let curSlide = 0;
// Functions
const createDots = function () {
slides.forEach(function (_, i) {
dotContainer.insertAdjacentHTML(
`beforeend`,
`<button class="dots__dot" data-slide="${i}"></button>`
);
});
};
const activateDot = function (slide) {
document
.querySelectorAll(`.dots__dot`)
.forEach((dot) => dot.classList.remove(`dots__dot--active`));
document
.querySelector(`.dots__dot[data-slide="${slide}"]`)
.classList.add(`dots__dot--active`);
};
const goToSlide = function (slide) {
slides.forEach(
(s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`)
);
};
// Next slide
const nextSlide = function () {
if (curSlide === maxSlide - 1) {
curSlide = 0;
} else {
curSlide++;
}
goToSlide(curSlide);
activateDot(curSlide);
};
const prevSlide = function () {
if (curSlide === 0) {
curSlide = maxSlide - 1;
} else {
curSlide--;
}
goToSlide(curSlide);
activateDot(curSlide);
};
const init = function () {
goToSlide(0);
createDots();
activateDot(0);
};
init();
// Event Handler
btnLeft.addEventListener(`click`, prevSlide);
btnRight.addEventListener(`click`, nextSlide);
document.addEventListener(`keydown`, function (e) {
console.log(e);
if (e.key === `ArrowLeft`) prevSlide();
e.key === `ArrowRight` && nextSlide();
});
dotContainer.addEventListener(`click`, function (e) {
if (e.target.classList.contains(`dots__dot`)) {
const { slide } = e.target.dataset;
goToSlide(slide);
activateDot(slide);
}
});
};
slider();
document.addEventListener(`DOMContentLoaded`, function (e) {
console.log(`HTML parsed and DOM tree built!`, e);
});
window.addEventListener(`load`, function (e) {
console.log(`Page fully loaded`, e);
});
window.addEventListener(`beforeunload`, function (e) {
e.preventDefault();
console.log(e);
e.returnValue = `message`;
});
END OF BODY
- 스크립트는 HTML이 완전히 파싱된 후에 가져와서 실행됩니다.
- 이전 브라우저를 지원해야 하는 경우 사용
⛔ 물론, 다른 스크립트에 대해 다른 전략을 사용할 수 있습니다. 보통 완전한 웹 애플리케이션에는 단 하나의 스크립트만 있는 것이 아니라 여러 개의 스크립트가 포함됩니다.
ASYNK IN HEAD
- 스크립트는 비동기적으로 가져와서 즉시 실행됩니다.
- 일반적으로 DOMContentLoaded 이벤트는 모든 스크립트의 실행을 기다리지만, async 스크립트는 제외됩니다. 따라서 DOMContentLoaded는 async 스크립트의 실행을 기다리지 않습니다.
- 스크립트의 실행 순서는 보장되지 않습니다
- 스크립트의 실행 순서가 중요하지 않은 경우에는 (예: Google Analytics와 같은) 제3자 스크립트에 사용합니다.
DEFER IN HEAD
- 스크립트는 비동기적으로 가져오며, HTML이 완전히 파싱된 후에 실행됩니다.
- DOMContentLoaded 이벤트는 defer 속성이 있는 스크립트가 실행된 후에 발생합니다.
- 스크립트는 일반적으로 순서대로 실행됩니다.
- 이것은 전반적으로 가장 좋은 해결책입니다! 이것은 자체 스크립트와 같이 스크립트의 순서가 중요한 경우 (예: 라이브러리 포함)에 사용됩니다.