“ 지연되는 프로젝트에 인력을 더 투입하면 오히려 더 늦어진다. ”
HTML
<header id="header">
<h1>Javascript parallax Effect01</h1>
<p>페럴랙스 이펙트 : 메뉴효과</p>
<ul>
<li><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 class="active"><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 id="parallax__wrap">
<section id="section1" class="parallax__item">
<span class="parallax__item__num">01</span>
<h2 class="parallax__item__title">sectin1</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">sectin2</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">sectin</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">sectin4</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">sectin5</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">sectin6</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">sectin7</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">sectin8</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">sectin9</h2>
<figure class="parallax_item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">꿈이 없는 사람은 아무 생명력이 없는 인형과 같다.</p>
</section><!-- //section9 -->
</div>
</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>
<!-- li*9{#section$ offset() : }>span.offset${px} -->
</ul>
</div>
</aside>
<footer id="footer">
<a href="mailto:jo0132@naver.com">jo0132@naver.com</a>
</footer>
- 이 HTML 문서에는 헤더, 내비게이션 바, 그리고 여러 개의 다양한 내용을 가진 섹션들이 포함되어 있습니다.
- 헤더에는 웹사이트의 제목, 설명, 그리고 다양한 페이지로의 링크를 포함한 내비게이션 메뉴가 있습니다. 현재 활성화된 페이지는 "active" 클래스가 해당 목록 항목에 추가되어 표시됩니다.
- 내비게이션 바는 "nav" 요소 내에 포함되어 있으며 페이지의 다양한 섹션으로의 링크 목록을 가지고 있습니다. 각 섹션은 ID (예: "#section1", "#section2" 등)로 식별됩니다.
- 페이지의 본문 내용은 "main" 요소에 포함되어 있으며 "main" 요소에는 ID가 "main"인 여러 개의 섹션이 있습니다. 각 섹션에는 제목, 이미지, 그리고 설명이 포함되어 있습니다.
CSS
#header {
position: absolute;
left: 20px;
top: 20px;
}
#header h1 {
margin-bottom: 0.3em;
}
#header ul {
margin-top: 0.6em;
}
#header li {
display: inline-block;
}
#header li a {
color: #fff;
border: 1px solid #fff;
width: 30px;
height: 30px;
line-height: 30px;
display: inline-block;
border-radius: 50%;
text-align: center;
}
#header li.active a {
background: #fff;
color: #000;
}
#footer {
text-align: center;
padding: 100px 0;
}
#footer a {
color: #fff;
font-size: 14px;
}
#footer a:hover {
text-decoration: underline;
}
/* parallax__nav */
.parallax__nav {
position: fixed;
right: 20px;
top: 20px;
z-index: 2000;
background-color: rgba(0,0,0,0.4);
padding: 20px 30px;
border-radius: 50px;
}
.parallax__nav li{
display: inline;
margin: 0 5px;
}
.parallax__nav li a {
display: inline-block;
padding: 5px 20px;
text-align: center;
line-height: 30px;
color: #fff;
}
.parallax__nav li.active a {
background-color: #fff;
color:#000;
border-radius: 20px;
box-sizing: content-box;
}
/* parallax__wrap */
.parallax__wrap {
max-width: 1600px;
width: 98%;
margin: 0 auto;
/* background-color: rgba(255,255,255,0.1); */
}
.parallax__item {
width: 1000px;
max-width: 70vw;
margin: 30vw auto;
/* background-color: rgba(255,255,255,0.3); */
margin-right: 0;
position: relative;
padding-top: 8vw;
}
.parallax__item:nth-child(even) {
margin-left: 0;
text-align: right;
}
.parallax__item__num {
font-size: 35vw;
font-weight: 100;
font-family: Lato;
position: absolute;
left: -5vw;
top: -16vw;
opacity: 0.07;
z-index: -2;
}
.parallax__item:nth-child(even) .parallax__item__num {
left: auto;
right: -5vw;
}
.parallax__item__title {
font-weight: bold;
}
.parallax_item__imgWrap {
width: 100%;
padding-bottom: 56.25%;
position: relative;
z-index: -1;
}
.parallax__item__img {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-image: url(../img/Effect_bg01-min.jpg);
background-size: cover;
filter: saturate(0%);
transition: all 1s;
}
.parallax__item:nth-child(1) .parallax__item__img {
background-image: url(../img/Effect_bg01-min.jpg);
}
.parallax__item:nth-child(2) .parallax__item__img {
background-image: url(../img/Effect_bg02-min.jpg);
}
.parallax__item:nth-child(3) .parallax__item__img {
background-image: url(../img/Effect_bg03-min.jpg);
}
.parallax__item:nth-child(4) .parallax__item__img {
background-image: url(../img/Effect_bg04-min.jpg);
}
.parallax__item:nth-child(5) .parallax__item__img {
background-image: url(../img/Effect_bg05-min.jpg);
}
.parallax__item:nth-child(6) .parallax__item__img {
background-image: url(../img/Effect_bg06-min.jpg);
}
.parallax__item:nth-child(7) .parallax__item__img {
background-image: url(../img/Effect_bg07-min.jpg);
}
.parallax__item:nth-child(8) .parallax__item__img {
background-image: url(../img/Effect_bg08-min.jpg);
}
.parallax__item:nth-child(9) .parallax__item__img {
background-image: url(../img/Effect_bg09-min.jpg);
}
.parallax__item:nth-child(10) .parallax__item__img {
background-image: url(../img/Effect_bg10-min.jpg);
}
.parallax__item__desc {
font-size: 4vw;
line-height: 1.4;
margin-top: -5vw;
margin-left: -4vw;
word-break: keep-all;
}
.parallax__item:nth-child(even) .parallax__item__desc {
margin-left: auto;
margin-right: -4VW;
}
.parallax__info {
position: fixed;
left: 20px;
bottom: 20px;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
padding: 20px;
border-radius: 10px;
line-height: 1.4;
}
@media (max-width: 1200px) {
.parallax__nav {
padding: 10px;
background-color: rgba(0,0,0,0.9);
right: 0;
left: 10px;
top: 10px;
border-radius: 5px;
text-align: center;
}
.parallax__nav li {
margin: 2px;
}
.parallax__nav li a {
font-size: 12px;
padding: 0px 14px;
}
}
- #header: id가 "header"인 요소에 대한 스타일을 정의합니다. "position: absolute;"는 해당 요소의 위치를 절대적으로 지정하고, "left: 20px;"와 "top: 20px;"은 해당 요소의 왼쪽과 위쪽 여백을 20px로 지정합니다.
- #header h1: #header 내의 h1 요소에 대한 스타일을 정의합니다. "margin-bottom: 0.3em;"은 해당 요소의 아래쪽 여백을 0.3em으로 지정합니다.
- #header ul: #header 내의 ul 요소에 대한 스타일을 정의합니다. "margin-top: 0.6em;"은 해당 요소의 위쪽 여백을 0.6em으로 지정합니다.
- #header li: #header 내의 li 요소에 대한 스타일을 정의합니다. "display: inline-block;"은 해당 요소를 가로로 나열하고, "margin: 0 5px;"은 해당 요소의 왼쪽과 오른쪽 여백을 5px로 지정합니다.
- #header li a: #header 내의 li 요소 안의 a 요소에 대한 스타일을 정의합니다. "color: #fff;"는 해당 요소의 글자 색상을 흰색으로 지정하고, "border: 1px solid #fff;"는 해당 요소의 테두리를 흰색 1px 실선으로 지정합니다. "width: 30px;"와 "height: 30px;"는 해당 요소의 가로와 세로 크기를 30px로 지정하고, "line-height: 30px;"는 해당 요소의 행 높이를 30px로 지정합니다. "border-radius: 50%;"는 해당 요소의 테두리를 둥글게 처리합니다. "text-align: center;"는 해당 요소의 텍스트를 가운데 정렬합니다.
- #header li.active a: #header 내의 활성화된 li 요소 안의 a 요소에 대한 스타일을 정의합니다. "background: #fff;"는 해당 요소의 배경색을 흰색으로 지정하고, "color: #000;"는 해당 요소의 글자 색상을 검은색으로 지정합니다.
JAVASCRIPT
window.addEventListener("scroll", () => {
let scrollTop = window.pageXOffset || 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(".parallax__info .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;
})
- window.addEventListener("scroll", () => { ... }); 코드는 scroll 이벤트를 감지하고, 스크롤 이벤트 발생 시 실행할 콜백 함수를 등록합니다.
- let scrollTop = window.pageXOffset || window.scrollY || document.documentElement.scrollTop; 코드는 현재 스크롤 위치를 확인합니다. window.pageXOffset, window.scrollY, document.documentElement.scrollTop 중에서 값이 있는 첫 번째 값을 scrollTop 변수에 저장합니다.
- document.querySelectorAll(".parallax__item").forEach((item,index) => { ... }); 코드는 .parallax__item 클래스를 가진 모든 요소에 대해 반복문을 실행합니다. 각 요소의 offsetTop 값을 비교하여 현재 스크롤 위치에 해당하는 내비게이션 메뉴를 활성화합니다.
- document.querySelectorAll(".parallax__nav li a").forEach(li => { ... }); 코드는 내비게이션 메뉴의 각 링크 요소에 대해 반복문을 실행합니다. 각 링크 요소에 클릭 이벤트를 등록하고, 클릭 시 해당 섹션으로 스크롤 이동을 구현합니다.
- document.querySelector(".parallax__info .scroll span").innerText = parseInt(scrollTop); 코드는 스크롤 위치 정보를 .parallax__info 클래스 내부의 .scroll 클래스 내부에 있는 span 요소에 업데이트합니다.
- document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop; 코드는 각 섹션의 offsetTop 정보를 .info 클래스 내부의 각 섹션에 해당하는 .offsetN 클래스 내부의 요소에 텍스트로 업데이트합니다.
for문
for(let i = 1; i < 10; i++){
document.querySelector(".info .offset"+i).innerText = document.getElementById("section"+i).offsetTop;
}
document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop; 을 for문으로 바꿀 수 있는데 .info .offsetX라는 클래스 이름을 가진 요소를 선택하고, 해당 요소의 innerText를 "sectionX"라는 id를 가진 요소의 offsetTop 속성값으로 설정하는 작업을 수행합니다. 이 코드는 HTML 문서에 .info 클래스와 .offset1부터 .offset9까지의 클래스 이름을 가진 요소, 그리고 "section1"부터 "section9"까지의 id를 가진 요소가 있다고 가정하고 실행되어야 합니다.
forEach()
forEach
document.querySelectorAll(".info span").forEach((span, index) => {
span.innerText = document.getElementById("section" + (index + 1)).offsetTop;
});
요소인 span에 띄우게 됩니다. span.innerText) (== span.innerText = document.getElementById("section" + (index + 1)).offsetTop;)
이 내용을 반복하는데 for문, forEach, for in, for of을 활용하는데
이 때 .info span을 반복해서 요소를 span으로 받고 index을 매개로 받아 사용합니다.
for in
const sectionIndex02 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i in sectionIndex02) {
document.querySelector(".info .offset" + sectionIndex02[i]).innerText = document.getElementById("section" + sectionIndex02[i]).offsetTop;
}
먼저 const sectionIndex02 = [1, 2, 3, 4, 5, 6, 7, 8, 9];라는 배열을 만들어고 시작합니다.
배열의 값을 통해 sectionIndex02[i]로 값을 받아 선택자에 원하는 수를 받아올수 있도록합니다. 그렇게 반복문을 작성하면 됩니다.
for of
const sectionIndex = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i of sectionIndex) {
document.querySelector(".info .offset" + i).innerText = document.getElementById("section" + i).offsetTop;
}
먼저 const sectionIndex = [1, 2, 3, 4, 5, 6, 7, 8, 9];라는 배열을 만들어고 시작합니다.
배열의 인덱스 값을 통해 i를 받아 선택자에 값을 받아올수 있도록합니다. 그렇게 반복문을 작성하면 됩니다.