개발하며 알게되는 빵부스러기를 기록한다. 빵부스러기 시리즈는 새롭게 알게되는 빵이 있을 때마다 최신화하도록 한다.
this.parentNode
⇒ this를 감싸고 있는 부모요소를 반환해준다.
this.nextElementSibling
⇒ this 의 다음에 따라오는 형제 요소를 반환해준다.
this.previousElementSibling
⇒ this 의 앞에 오는 형제 요소를 반환해준다.
this.children
⇒ this의 자식 요소들을 리스트(배열) 형태로 반환해준다.
this.textContent
⇒ 선택한 요소(this)의 text 값이 들어있다면 text를 반환해준다.
this.classList.contains("className")
⇒ 클래스가 있는지 없는지에 따라 boolean값을 반환한다.
e.target
⇒ 이벤트의 종류를 클릭으로 예를 들자면, 내가 클릭한 요소가 무엇이지? ⇒ e.target , 즉 클릭한 DOM Element의 정보를 리턴해준다.
e.stopPropagation()
⇒ 이벤트는 기본적으로 전파가 된다.(작은부분에서 큰 부분으로),e.stopPropagation()
은 이벤트 전파를 멈추게 한다.
this(parent).removeChild(child: Node)
⇒ 기억해야할 포인트는 DOM에서 어떤 요소를 삭제할 때 그 권한은 내가 삭제 하고자 하는 요소의 부모한테만 있다는 것. 부모요소안에 포함되어있는 선택한 자식 요소를 삭제해준다.
this.appendChild(chile: Node)
⇒ 요소를 추가할 때도 마찬가지로, 추가하고자 하는 요소를 감싸고 있는 부모에게 작업을 해주도록 해야한다. ⇒append
는 ‘추가하다’ 라는 뜻이며 그냥 사용할 시에는 부모 요소안의 마지막 자식으로 들어가게 된다.
this.prepend(child: Node)
⇒appendChild
와 동일하지만 마지막 자식이 아니라 첫 번째 자식으로 들어간다.prepend
는 ‘앞에 붙히다’ 라는 뜻을 가진다.
this.contains(”선택한 명시적 요소”)
⇒ this가 “선택한 명시적 요소”를 포함하고 있는지 없는지에 따라 boolean 값을 리턴해준다. 밑의 설명 참고
const myMenu = document.querySelector('.my-menu')
const myMenuButton = document.querySelector('.my-menu-button')
function closeMyMenuOnClickingOutside(e) {
if (!myMenu.contains(e.target)) { // myMenu가 e.target을 포함하고 있어? 라고 물어보는것. 그냥 myMenu안에 포함되어있는지(자식~손자 요소인지)를 묻는 것이다.
myMenu.classList.remove('is-active')
window.removeEventListener('click', closeMyMenuOnClickingOutside)
}
}
function toggleMyMenu() {
if (!myMenu.classList.contains('is-active')) { // classList.contains는 위와 다른 경우, 단순히 myMenu가 "is-active" 라는 클래스명을 가지고 있는지를 판단하는 것임
window.addEventListener('click', closeMyMenuOnClickingOutside)
}
myMenu.classList.toggle('is-active')
}
myMenuButton.addEventListener('click', toggleMyMenu)
this.setAttribute("attributeName", "attributeValue")
⇒ this의 속성값을 설정해준다. 밑의 코드를 참고
Element.setAttribute(qualifiedName: string, value: string): void
// 첫 번째 인자로는 내가 어떤 attribute을 바꾸고 싶은지를 명시하기 위한 이름을 적는다.
// 두 번째 인자로는 위 명시한 attribute에 어떤 값을 넣고 싶은지에 대한 값을 넣어준다.
// setAttribute은 굉장히 다양하게 사용할 수가 있다.
// 그래서 만약 class를 바꾸고 싶을 때에도 classList 메소드를 쓰지 않고도 setAttribute을 이용하여 바꿀 수 있다.
// 다음 예시를 봐보자.
countSpan.setAttribute('class', 'hi')
// 나는 class 라고 하는 속성을 바꿔줄건데, 뭐로 바꿔줄 거냐면 hi로 클래스이름을 바꿔줄거야
// 애초에 class 가 없다고 하면 class를 추가해준다.
this.getAttribute("attributeName")
⇒ setAttribute와 반대로 어떠한 요소가 어떠한 속성값을 가지고 있는지 알고 싶을 때 사용한다.attributeName
은 값을 얻고자 하는 속성의 이름이다. 밑의 코드를 참고
let div1 = document.getElementById("div1");
let align = div1.getAttribute("align");
alert(align); // id가 "div1"인 요소(element)의 align 값을 보여줍니다.
---
const TOP_HEADER_DESKTOP = 80 + 50 + 54
const TOP_HEADER_MOBILE = 50 + 40 + 40
const tabPanelId = this.parentNode.getAttribute('aria-labelledby');
console.log(tabPanelId); // product-review
const tabPanel = document.querySelector(`#${tabPanelId}`)
const scrollAmount =
tabPanel.getBoundingClientRect().top -
(window.innerWidth >= 768 ? TOP_HEADER_DESKTOP : TOP_HEADER_MOBILE)
window.scrollBy({
top: scrollAmount,
behavior: 'smooth',
})
String.replaceAll()
⇒ 밑의 설명 참고
const a = 'aaaaaaabbbbbbccccccddddd'
a.replace('b', 'z') // 나는 a가 갖고있는 b를 z로 바꿔주겠어!
console.log(a) // aaaaaaazbbbbbccccccddddd
// 제일 처음에 만나는 b 를 z로 바꿔준다.
// 근데 만약 b의 전체를 다 z로 바꿔주고 싶다면 ? => replaceAll 을 사용하자.
a.replaceAll('b', 'z')
console.log(a) // aaaaaaazzzzzzccccccddddd
// 다음 예시도 봐보자.
function toggleOrderCtaBookmark() {
const [icon, countSpan] = this.children
console.log(countSpan) // '18,302'
const count = Number(countSpan.innerHTML.replaceAll(',', '')) // 콤마가 있으면 지워줘 그리고 숫자로 변환해줘
console.log(count) // 18302
let newCount = count
if (this.classList.contains('is-active')) {
newCount = newCount - 1
} else {
newCount = newCount + 1
}
countSpan.innerHTML = newCount.toLocaleString() // 다시 콤마를 넣어준다.
countSpan.setAttribute('aria-label', `북마크 ${newCount.toLocaleString()}회`)
// 내가 지금 바꾸고 싶은 attribute은 'aria-label' 이고, 뭐로 바꾸고 싶냐면 `북마크 ${newCount.toLocaleString()}회`로 바꾸고 싶다.
// ...생략
}
Number.toLocaleString()
⇒ 밑의 설명 참고
Number.toLocaleString(locales?: string | string[], options?: Intl.NumberFormatOptions): string
---
const a = 21342583425252134
console.log(a.toLocaliString()) // '21,342,583,425,252,136'
// toLocaliString()은 알아서 콤마를 알맞은 위치에 넣어주어 포맷팅해주는 자바스크립트 메소드다.
// 다만 스트링 타입으로 리턴하니 필요하다면 숫자 타입으로 바꿔주면 된다.
Element.getBoundingClientRect()
⇒ Viewport 화면 내에서 명시한 요소의 위치가 어디에 위치에있는지에 대한 정보를 반환해준다. 즉, 어떠한 요소가 뷰포트를 기준으로 y축으로 x축으로 얼마나 떨어져 있는지에 대한 정보를 반환해준다. 밑의 이미지와 코드 참고
function scrollToTabPanel() {
const tabPanelId = this.parentNode.getAttribute('aria-labelledby')
const tabPanel = document.querySelector(`#${tabPanelId}`)
const scrollAmount =
tabPanel.getBoundingClientRect().top -
(window.innerWidth >= 768 ? TOP_HEADER_DESKTOP : TOP_HEADER_MOBILE)
window.scrollBy({
top: scrollAmount,
behavior: 'smooth',
})
}
window.innerWidth
⇒ 유저가 현재 보고있는 창의 크기가 얼마인지 알 수 있다. 밑의 코드 참고.
const scrollAmount =
tabPanel.getBoundingClientRect().top -
(window.innerWidth >= 768 ? TOP_HEADER_DESKTOP : TOP_HEADER_MOBILE)
window.innerHeight
⇒ 뷰포터의 세로 길이 값을 알 수 있다.
window.scrollY
⇒ 유저가 Y 축으로 스크롤을 얼마나 했는 지 알 수 있다.
window.scrollY
+Element.getBoundingClientRect().top
은 해당 요소가 문서의 시작점에서부터 얼만큼 아래에 위치하고 있는지에 대한 정보를 반환한다. 즉 요소의 y축 위치를 항상 동일한 값으로 반환해준다.
또한 이를 활용하면, 만약 유저가 스크롤을 제일 아래까지 내렸을 경우,
window.scrollY
+window.innerHeight
의 값은 전체 웹페이지 body의 heigth와 같다는 것을 알 수 있다. 밑의 설명 참고
이를 반대로 생각해보면 window.scrollY
+ window.innerHeight
의 값이 전체 웹페이지의 height
와 같다면, 유저가 현재 웹페이지의 제일 아래 부분까지 스크롤을 했다는 것을 알 수 있다.
그렇다면 웹 페이지 전체 body
의 height
는 어떻게 구할까 ?
document.body.offsetHeight
=> 전체 문서body
의 총 길이를 알려준다.
다만 만약 태블릿이나 모바일과 같은 환경에서 order-CTA 와 같은 컴포넌트가 bottom
에 fixed
된 방식으로 표시되어지고 있고 또한 이 컴포넌트에 특정한 마진값이 설정되어있다면 마진값은 따로 사이즈를 가지고 있다고 보지 않기때문에 이를 적절히 계산해주어야 한다. 밑의 코드 참고.
const bodyHeight =
document.body.offsetHeight + (window.innerWidth < 1200 ? 56 : 0)
// 태블릿 사이즈부터는 margin-bottom: 56px 이 주어지기 때문에, 56px을 따로 추가해준 경우이다. 데스크탑은 margin-bottom 이 0으로 설정되어 있다.