“ 당신이 6개월 이상 한 번도 보지 않은 코드는 다른 사람이 다시 만드는 게 훨씬 더 나을 수 있다. ”
패럴랙스 이펙트란?
패럴랙스 이펙트는 사용자가 스크롤을 내리거나 올릴 때, 배경과 함께 웹 페이지의 요소들이 서로 다른 속도로 움직이는 것을 말합니다. 이는 일반적으로 CSS와 JavaScript를 사용하여 구현되며, 웹 페이지를 더욱 동적이고 인터랙티브하게 만들어줍니다.
웹사이트 패럴랙스 이펙트를 사용하면 사용자가 웹 페이지를 스크롤 할 때, 배경 이미지와 함께 다양한 요소들이 움직이는 것처럼 보입니다. 예를 들어, 웹 페이지에 일러스트나 이미지를 배경으로 사용하면 이를 이용하여 사용자의 눈길을 이끌거나 페이지의 구조를 강조할 수 있습니다. 또는 텍스트나 버튼과 같은 요소들도 패럴랙스 이펙트를 이용하여 동적인 효과를 줄 수 있습니다.
패럴랙스 이펙트는 웹 디자인의 트렌드 중 하나로, 사용자 경험을 개선하고 웹 페이지를 더욱 흥미롭고 동적으로 만들어줍니다. 하지만, 지나친 사용은 사용자 경험을 해치고 성능 저하를 일으킬 수 있으므로 적절한 사용이 필요합니다.
패럴랙스 이펙트 사이트 코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>패럴랙스 이펙트01</title>
<link rel="stylesheet" href="css/parallax.css">
<link rel="stylesheet" href="css/reset.css">
</head>
<body class="img12 bg01 font02">
<header id="header">
<h1>Javascript Parallax Effect01</h1>
<p>패럴랙스 이펙트 : 메뉴 효과</p>
<ul>
<li class="active"><a href="parallaxEffect01.html">1</a></li>
<li><a href="parallaxEffect02.html">2</a></li>
<li><a href="parallaxEffect03.html">3</a></li>
<li><a href="parallaxEffect04.html">4</a></li>
<li><a href="parallaxEffect05.html">5</a></li>
<li><a href="parallaxEffect06.html">6</a></li>
<li><a href="parallaxEffect07.html">7</a></li>
</ul>
</header>
<!-- //header -->
<nav class="parallax__nav">
<ul>
<li class="active"><a href="#section1">메뉴1</a></li>
<li><a href="#section2">메뉴2</a></li>
<li><a href="#section3">메뉴3</a></li>
<li><a href="#section4">메뉴4</a></li>
<li><a href="#section5">메뉴5</a></li>
<li><a href="#section6">메뉴6</a></li>
<li><a href="#section7">메뉴7</a></li>
<li><a href="#section8">메뉴8</a></li>
<li><a href="#section9">메뉴9</a></li>
</ul>
</nav>
<main id="main">
<div class="parallax__wrap">
<section id="section1" class="parallax__item">
<span class="parallax__item__num">01</span>
<h2 class="parallax__item__title">Section1</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">행동이 모든 것을 결정한다. 운명은 행동에서 비롯된다.</p>
</section>
<!-- //section1 -->
<section id="section2" class="parallax__item">
<span class="parallax__item__num">02</span>
<h2 class="parallax__item__title">Section2</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">작은 성공이 큰 성공으로 이어진다.</p>
</section>
<!-- //section2 -->
<section id="section3" class="parallax__item">
<span class="parallax__item__num">03</span>
<h2 class="parallax__item__title">Section3</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">인생은 결코 공평하지 않다. 그러나 그것을 이길 수 있다.</p>
</section>
<!-- //section3 -->
<section id="section4" class="parallax__item">
<span class="parallax__item__num">04</span>
<h2 class="parallax__item__title">Section4</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">성취하지 못한 것은 아직 해보지 않은 것뿐입니다.</p>
</section>
<!-- //section4 -->
<section id="section5" class="parallax__item">
<span class="parallax__item__num">05</span>
<h2 class="parallax__item__title">Section5</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">가장 높은 산은 한 발짝부터 시작된다.</p>
</section>
<!-- //section5 -->
<section id="section6" class="parallax__item">
<span class="parallax__item__num">06</span>
<h2 class="parallax__item__title">Section6</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">목표를 향한 첫 걸음은 용기이다.</p>
</section>
<!-- //section6 -->
<section id="section7" class="parallax__item">
<span class="parallax__item__num">07</span>
<h2 class="parallax__item__title">Section7</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">우리의 인생은 우리의 생각과 믿음이 결정한다.</p>
</section>
<!-- //section7 -->
<section id="section8" class="parallax__item">
<span class="parallax__item__num">08</span>
<h2 class="parallax__item__title">Section8</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">언제나 새로운 것을 배울 수 있고, 새로운 것을 경험할 수 있다.</p>
</section>
<!-- //section8 -->
<section id="section9" class="parallax__item">
<span class="parallax__item__num">09</span>
<h2 class="parallax__item__title">Section9</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">한계는 우리가 만든 것이며, 우리가 이겨낼 수 있다.</p>
</section>
<!-- //section9 -->
</div>
</main>
<!-- //main -->
<aside class="parallax__info">
<div class="scroll">scrollTop : <span>0</span>px</div>
<div class="info">
<ul>
<li>#section1 offset() : <span class="offset1">0</span>px</li>
<li>#section2 offset() : <span class="offset2">0</span>px</li>
<li>#section3 offset() : <span class="offset3">0</span>px</li>
<li>#section4 offset() : <span class="offset4">0</span>px</li>
<li>#section5 offset() : <span class="offset5">0</span>px</li>
<li>#section6 offset() : <span class="offset6">0</span>px</li>
<li>#section7 offset() : <span class="offset7">0</span>px</li>
<li>#section8 offset() : <span class="offset8">0</span>px</li>
<li>#section9 offset() : <span class="offset9">0</span>px</li>
</ul>
</div>
</aside>
<!-- parallax__info -->
script 코드
<script>
window.addEventListener("scroll", () => {
let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;
document.querySelectorAll(".parallax__item").forEach((item, index) => {
if(scrollTop >= item.offsetTop - 2){
document.querySelectorAll(".parallax__nav li").forEach((li) => {
li.classList.remove("active");
});
document.querySelector(".parallax__nav li:nth-child("+(index+1)+")").classList.add("active");
}
});
document.querySelectorAll(".parallax__nav li a").forEach(li => {
li.addEventListener("click", (e) => {
e.preventDefault();
document.querySelector(li.getAttribute("href")).scrollIntoView({
behavior: "smooth"
});
});
});
//info
document.querySelector(".scroll span").innerText = parseInt(scrollTop);
document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop;
document.querySelector(".info .offset2").innerText = document.getElementById("section2").offsetTop;
document.querySelector(".info .offset3").innerText = document.getElementById("section3").offsetTop;
document.querySelector(".info .offset4").innerText = document.getElementById("section4").offsetTop;
document.querySelector(".info .offset5").innerText = document.getElementById("section5").offsetTop;
document.querySelector(".info .offset6").innerText = document.getElementById("section6").offsetTop;
document.querySelector(".info .offset7").innerText = document.getElementById("section7").offsetTop;
document.querySelector(".info .offset8").innerText = document.getElementById("section8").offsetTop;
document.querySelector(".info .offset9").innerText = document.getElementById("section9").offsetTop;
});
</script>
window 객체에 scroll 이벤트를 추가하고, 스크롤이 발생할 때마다 콜백 함수를 실행합니다. 콜백 함수 내부에서는 let 키워드를 사용하여 scrollTop 변수를 선언하고, window.pageYOffset 또는 window.scrollY 또는 document.documentElement.scrollTop 값을 가져옵니다. 이 값은 현재 스크롤 위치를 나타내며, pageYOffset과 scrollY는 호환성을 위해 브라우저에 따라 다른 이름으로 사용될 수 있습니다.
scrollTop 변수는 이후에 콜백 함수 내에서 스크롤 위치를 사용하여 다른 요소를 조작하는 데 사용됩니다. 스크롤 위치를 가져오는 방법은 다양하며, 호환성을 고려하여 브라우저에서 가장 잘 동작하는 방법을 사용하는 것이 좋습니다.
document.querySelectorAll(".parallax__item").forEach((item, index) => {
if(scrollTop >= item.offsetTop - 2){
document.querySelectorAll(".parallax__nav li").forEach((li) => {
li.classList.remove("active");
});
document.querySelector(".parallax__nav li:nth-child("+(index+1)+")").classList.add("active");
}
});
document.querySelectorAll 메소드를 사용하여 parallax__item 클래스를 가진 모든 요소를 선택하고, forEach 메소드를 사용하여 각 요소에 대한 콜백 함수를 실행합니다. 콜백 함수는 item과 index 매개변수를 가지며, item은 각각의 parallax__item 요소이고, index는 해당 요소의 인덱스입니다.
콜백 함수 내에서는 if문을 사용하여 스크롤 위치인 scrollTop 값이 각 요소의 offsetTop 값보다 크거나 같은 경우를 확인합니다. offsetTop 값은 요소가 문서의 맨 위에서부터 얼마나 떨어져 있는지를 나타내며, if문은 스크롤 위치가 각 요소의 상단에 도달했을 때 실행됩니다.
if문 내부에서는 document.querySelectorAll 메소드를 사용하여 parallax__nav li 클래스를 가진 모든 요소를 선택하고, forEach 메소드를 사용하여 각 요소에 대한 콜백 함수를 실행합니다. 이 콜백 함수는 li 매개변수를 가지며, 각각의 parallax__nav li 요소입니다. 콜백 함수 내에서는 classList.remove 메소드를 사용하여 active 클래스를 모두 제거한 후, querySelector 메소드를 사용하여 해당 요소를 선택하고 classList.add 메소드를 사용하여 active 클래스를 추가합니다.
결과적으로, 스크롤 위치에 따라 parallax__nav li 요소 중 하나에 active 클래스가 추가되고, 나머지 요소에서는 active 클래스가 제거됩니다. 이것은 스크롤에 따라 현재 화면에 보이는 parallax__item 요소를 표시하기 위한 것으로 보입니다.
document.querySelectorAll(".parallax__nav li a").forEach(li => {
li.addEventListener("click", (e) => {
e.preventDefault();
document.querySelector(li.getAttribute("href")).scrollIntoView({
behavior: "smooth"
});
});
});
ocument.querySelectorAll(".parallax__nav li a")를 통해 .parallax__nav 클래스에 속한 li 요소 안에 있는 a 요소를 모두 선택합니다. 그리고 forEach 함수를 사용하여 선택된 모든 a 요소에 대해 이벤트 리스너를 등록합니다.
이벤트 리스너는 click 이벤트를 수신하면 실행되며, 이벤트 객체의 preventDefault() 메서드를 호출하여 링크를 클릭해도 기본적인 동작을 수행하지 않도록 막습니다.
li.getAttribute("href")를 사용하여 클릭된 a 요소의 href 속성 값을 가져오고, 이 값을 document.querySelector 함수를 사용하여 해당 요소를 선택합니다. 그리고 선택된 요소의 scrollIntoView 메서드를 사용하여 해당 요소로 부드럽게 스크롤합니다. 이때, behavior: "smooth" 옵션을 사용하여 부드러운 스크롤 효과를 적용합니다.
//info
document.querySelector(".scroll span").innerText = parseInt(scrollTop);
document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop;
document.querySelector(".info .offset2").innerText = document.getElementById("section2").offsetTop;
document.querySelector(".info .offset3").innerText = document.getElementById("section3").offsetTop;
document.querySelector(".info .offset4").innerText = document.getElementById("section4").offsetTop;
document.querySelector(".info .offset5").innerText = document.getElementById("section5").offsetTop;
document.querySelector(".info .offset6").innerText = document.getElementById("section6").offsetTop;
document.querySelector(".info .offset7").innerText = document.getElementById("section7").offsetTop;
document.querySelector(".info .offset8").innerText = document.getElementById("section8").offsetTop;
document.querySelector(".info .offset9").innerText = document.getElementById("section9").offsetTop;
document.querySelector(".scroll span").innerText = parseInt(scrollTop); 코드는 현재 스크롤 위치를 .scroll 클래스 안에 있는 span 요소의 텍스트로 출력합니다. scrollTop 변수는 현재 스크롤 위치를 나타내는 값입니다. parseInt() 함수를 사용하여 소수점 이하의 값을 버리고 정수형으로 변환하여 출력합니다.
document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop; 코드는 #section1 아이디를 가진 요소의 위치 정보를 .info 클래스 안에 있는 .offset1 클래스의 텍스트로 출력합니다. offsetTop 속성은 요소의 상위 요소로부터의 거리를 나타내는 값입니다.
이와 같은 방식으로, offset2에서 offset9까지의 클래스를 각 섹션의 위치 정보로 바꿔가며, 각 섹션 요소의 위치 정보를 출력합니다.
위 코드를 아래 코드 처럼 for문으로 써줄 수 있습니다.
for문으로 작성시 코드 길이가 간단해지고 코드 수정이 용이해지며 코드 유지보수가 쉬워집니다.
for문
for (let i=1; i <=9; i++) {
document.querySelector(".offset" + i).innerText = document.getElementById("section" + i).offsetTop;
}
for문은 let i=1에서 시작하여 i가 9보다 작거나 같을 때까지 반복됩니다. i는 1씩 증가하며, 이 값은 각 섹션의 번호를 나타내는데 사용됩니다.
".offset" + i는 현재 반복문에서 i의 값에 따라 .offset1부터 .offset9까지의 클래스 이름을 생성합니다. 이후, document.querySelector 메서드를 사용하여 각 클래스 이름에 해당하는 요소를 찾아서 .innerText 속성을 사용하여 해당 요소의 텍스트를 출력합니다.
"section" + i는 현재 반복문에서 i의 값에 따라 "section1"부터 "section9"까지의 아이디를 생성합니다. 이후, document.getElementById 메서드를 사용하여 각 아이디에 해당하는 요소의 offsetTop 속성을 가져옵니다.
forEach
document.querySelectorAll(".info ul li").forEach((el, i)=>{
document.querySelector(".info .offset"+(i+1)).innerText = document.getElementById("section"+(i+1)).offsetTop
});
document.querySelectorAll(".info ul li")는 모든 ul 요소의 자식 li 요소를 선택합니다. 그리고 forEach 메서드를 사용하여 각 li 요소에 대해 함수를 실행합니다. 함수의 첫 번째 매개변수는 현재 li 요소이며, 두 번째 매개변수는 현재 인덱스(i)입니다.
함수 안에서는 document.querySelector 메서드를 사용하여 .info .offset1부터 .info .offset9까지의 요소를 찾습니다. 이 때, i는 0부터 시작하는 배열 인덱스이므로, 각 클래스 이름에 +1을 해주어야 해당하는 요소를 찾을 수 있습니다. 이후, document.getElementById 메서드를 사용하여 "section1"부터 "section9"까지의 요소의 offsetTop 속성을 가져와서 각 .offset 요소의 텍스트로 출력합니다.
장점
querySelectorAll 메서드와 forEach 메서드를 사용하여 각 섹션의 위치 정보를 출력하는 코드로, 간단하고 가독성이 좋습니다.
for in
for(let i in document.querySelectorAll(".info ul li")){
const el = document.querySelectorAll(".info ul li")[i];
document.querySelector(".info .offset"+(parseInt(i)+1)).innerText = document.getElementById("section"+(parseInt(i)+1)).offsetTop;
}
document.querySelectorAll(".info ul li")는 모든 ul 요소의 자식 li 요소를 선택합니다. for...in문에서 i는 이러한 선택된 요소의 인덱스가 됩니다. 각 li 요소에 대해 함수를 실행합니다.
함수 안에서는 document.querySelector 메서드를 사용하여 .info .offset1부터 .info .offset9까지의 요소를 찾습니다. 이 때, i는 문자열로 처리되므로, parseInt 함수를 사용하여 숫자로 변환한 후, 각 클래스 이름에 +1을 해주어야 해당하는 요소를 찾을 수 있습니다. 이후, document.getElementById 메서드를 사용하여 "section1"부터 "section9"까지의 요소의 offsetTop 속성을 가져와서 각 .offset 요소의 텍스트로 출력합니다.
장점
for...in문을 사용하여 각 섹션의 위치 정보를 출력하는 코드로, querySelector와 getElementById 메서드를 반복하지 않으므로 코드가 간소화되었으며 가독성이 좋아졌습니다.
단점
for...in문은 배열이 아닌 객체에 사용할 때 예상치 못한 결과가 발생할 수 있으므로 주의가 필요합니다.
for of
for (const [i, el] of document.querySelectorAll(".info ul li span").entries()){
el.innerHTML = document.getElementById("section"+(parseInt(i)+1)).offsetTop;
}
document.querySelectorAll(".info ul li span")는 .info ul 요소의 모든 자식 li 요소의 자식 span 요소를 선택합니다. entries() 메서드는 배열의 각 요소에 대한 인덱스와 값을 모두 포함하는 새로운 배열 반복자(iterator)를 반환합니다. for...of문에서 [i, el]은 이러한 반복자의 인덱스와 값이 됩니다.
반복문 안에서는 document.getElementById 메서드를 사용하여 "section1"부터 "section9"까지의 요소의 offsetTop 속성을 가져와서 각 span 요소의 텍스트로 출력합니다. 이 때, i는 문자열로 처리되므로, parseInt 함수를 사용하여 숫자로 변환한 후, "section"+(parseInt(i)+1)을 사용하여 해당하는 요소를 찾을 수 있습니다.
장점
for...of문과 entries() 메서드를 사용하여 각 섹션의 위치 정보를 출력하는 코드로, 코드가 간소화되고 가독성이 좋아졌습니다. 이 코드에서는 querySelector 메서드를 사용하지 않아도 되기 때문에, querySelector와 getElementById 메서드를 사용하는 다른 코드보다 더욱 간결해졌습니다.