1) visual 영역 구현
1-1) 이미지 2개 슬라이드 구현
1-2) 이미지 3개 슬라이드 및 preview / next 버튼 구현
1-3) 실행 순서
1) visual 영역 구현
1-1) 이미지 2개 슬라이드 구현
(중요!!) classList와 className의 차이점
- classList는 add와 remove를 써서 기존 class가 있다면 해당 class 이름은 그냥 둔 상태(즉, 기존에 이미 존재하던 class 이름을 수정하지 않는다는 것이 point임!!)에서 새로운 이름을 추가(add)하거나 삭제(remove)할 수 있음!!
- className은 기존에 이미 class 이름이 존재한다면 해당 class 이름을 새로 할당한 이름으로 바꿔줌(즉, 기존에 이미 존재하던 class 이름을 완전히 바꿔버린다는 것이 point임!!)!!
index.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
<link rel="stylesheet" href="./public/css/index.css" />
<script src="./public/js/index.js" type="text/javascript"></script>
</head>
<body>
<ul id="visual">
<li>
<img
src="http://www.kiweb.or.kr/nBoard/upload/file/main/1666596927_56073_1.jpg"
alt="img1"
/>
</li>
<li>
<img
src="http://www.kiweb.or.kr/nBoard/upload/file/main/1661838619_28666_1.png"
alt="img2"
/>
</li>
</ul>
</body>
</html>
index.css
* {
margin: 0;
padding: 0;
}
ul,
li {
list-style: none;
}
#visual {
position: relative;
width: 1900px;
height: 500px;
margin: 0 auto;
overflow: hidden;
/* overflow: hidden;을 적용한 element의 영역을 해당 element의 자식 element가 넘어갈 시 넘어간 부분은 브라우저 화면에 보이지 않게 함!!(단, 보이지 않는 부분은 완전히 사라진 것이 아니라 브라우저 화면에 안 보일 뿐 존재는 하고 있음!!) */
}
#visual > li {
position: absolute;
transition: all 3s;
}
#visual > li:nth-child(1) {
left: 0;
}
#visual > li:nth-child(2) {
left: 1920px;
}
#visual > li.on {
left: 0;
}
#visual > li.off {
left: -1920px;
}
/* hover를 써서 커서를 갔다댔을 시에만 이미지가 이동하도록 하게 하는 코드 */
/*
#visual:hover > li.on {
left: 0;
}
#visual:hover > li.off {
left: -1920px;
}
#visual > li.on {
animation-name: on; >> 일종의 변수같은 개념
animation-duration: 0.5s; >> 애니메이션 지속 시간 설정(마치 transition의 애니메이션 적용 시간을 정하는 것과 같은 역할로 보면 됨)
}
#visual > li.off {
animation-name: off;
animation-duration: 0.5s;
}
@keyframes는 위의 animation-name(마치 자바스크립트의 변수와 같다고 이해하면 됨!!)의 값과 일치시겨 서로를 연결시켜 애니메이션 효과를 주는 방식이다!!
"0%"는 처음 시작 지점을, "100%"는 최종 완료 지점을 의미함
@keyframes on {
0% {
left: 1920px;
}
100% {
left: 0;
}
}
@keyframes off {
0% {
left: 0;
}
100% {
left: -1920px;
}
}
*/
index.js
function init() {
const imgs = document.querySelectorAll("#visual > li");
let count = 0;
// setInterval 콜백함수 slide
function slide() {
if (count % 2 === 0) {
imgs[0].className = "off";
imgs[1].className = "on";
imgs[1].style = "";
} else {
imgs[1].style = "left:1920px";
imgs[0].className = "on";
imgs[1].className = "off";
}
// console.log(count); // count 값 증가 확인용
count++;
if (count === 2) {
count = 0;
}
}
setInterval(slide, 3000);
}
document.addEventListener("DOMContentLoaded", init);
1-2) 이미지 3개 슬라이드 및 preview / next 버튼 구현
test.html
<!DOCTYPE html>
<html lang="en">
<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>Document</title>
<link rel="stylesheet" href="./public/css/test.css" />
<script src="./public/js/test.js" type="text/javascript"></script>
</head>
<body>
<ul id="visual">
<li>
<img
src="http://www.kiweb.or.kr/nBoard/upload/file/main/1666596927_56073_1.jpg"
alt="img1"
/>
</li>
<li>
<img
src="http://www.kiweb.or.kr/nBoard/upload/file/main/1661838619_28666_1.png"
alt="img2"
/>
</li>
<li>
<img
src="http://www.kiweb.or.kr/nBoard/upload/file/main/1661838462_26399_1.png"
alt="img3"
/>
</li>
</ul>
<ul id="visualBtn">
<li><button>버튼1</button></li>
<li><button>버튼2</button></li>
<li><button>버튼3</button></li>
</ul>
<ul>
<li class="prev"><button>이전</button></li>
<li class="next"><button>다음</button></li>
</ul>
</body>
</html>
test.css
* {
margin: 0;
padding: 0;
}
ul,
li {
list-style: none;
}
#visual {
position: relative;
width: 1900px;
height: 500px;
margin: 0 auto;
overflow: hidden;
}
#visual > li {
position: absolute;
transition: all 1s;
opacity: 0;
}
#visual > li.on {
opacity: 1;
}
test.js
let intervalId = 0;
let count = 0;
function init() {
// visual : Element를 반환받음(querySelector는 Element를 반환하기 때문)
// const visual = document.querySelector("#visual");
// imgs => Array를 반환받음(querySelectorAll은 Element들을 배열로 반환하기 때문)
// imgs 데이터 타입(Array) : Element[] => [Element, Element]
const imgs = document.querySelectorAll("#visual > li");
const btns = document.querySelectorAll("#visualBtn > li"); // Element[]
const prevBtn = document.querySelector(".prev");
const nextBtn = document.querySelector(".next");
function findIndex() {
// ["경일", "게임", "아카데미"]의 배열에서 "게임" 요소만 뽑아내는 것과 똑같은 case
for (let i = 0; i < imgs.length; i++) {
console.log(imgs[i].getAttribute("class")); // on
if (imgs[i].getAttribute("class") === "on") {
return i;
}
}
}
// setInterval
function slide() {
// console.log("현재 인덱스 : ", count);
// console.log("이전 인덱스 : ", count - 1);
let prev = count === 0 ? imgs.length - 1 : count - 1;
imgs[count].className = "on";
imgs[prev].className = "";
// console.log(count); // count 값 증가 확인용
count++;
if (count === 3) {
count = 0;
}
// class = "a b c"
// "a b c".split(" ") => ["a", "b", "c"] => classList
// classList add, remove
// ["a", "b", "c"]의 배열에서 "a" 요소 빼기 => splice 활용
// ["a", "b", "c"].splice(0, 1) => ["b", "c"]
// add와 push는 둘 다 새로운 요소 추가 가능
// ["a", "b", "c"].add("d") = ["a", "b", "c"].push("d")
}
// 버튼 영역
// Event를 설정하려면 기본적으로 Element를 가져와야 함
// querySelectorAll을 써서 Element 가져오기
// const btns = document.querySelectorAll("#visualBtn > li"); // Element[]
// addEventListener의 2번째 매개변수인 콜백함수의 매개변수는 우리가 조작할 수 없기 때문에 이를 조작하기 위해 아래와 같이 고차함수를 사용함!!
function btnHandler(index) {
return function (e) {
clearInterval(intervalId);
const current = findIndex();
imgs[current].className = "";
imgs[index].className = "on";
count = index;
// setInterval : 데이터 타입(number)
intervalId = setInterval(slide, 2000);
};
}
function prevHandler(e) {
clearInterval(intervalId);
const current = findIndex();
const index = current === 0 ? imgs.length - 1 : current - 1;
imgs[current].className = "";
imgs[index].className = "on";
count = index;
intervalId = setInterval(slide, 2000);
}
function nextHandler(e) {
clearInterval(intervalId);
const current = findIndex();
const index = current === imgs.length - 1 ? 0 : current + 1;
imgs[current].className = "";
imgs[index].className = "on";
count = index;
intervalId = setInterval(slide, 2000);
}
for (let i = 0; i < btns.length; i++) {
// btns[i]의 데이터 타입 = Element
btns[i].addEventListener("click", btnHandler(i));
}
prevBtn.addEventListener("click", prevHandler);
nextBtn.addEventListener("click", nextHandler);
slide(); // 브라우저 load가 완료되지마자 첫번째 이미지에 class 이름(on)을 붙여주기 위한 코드
intervalId = setInterval(slide, 3000);
}
document.addEventListener("DOMContentLoaded", init);
1-3) 실행 순서
let intervalId = 0;
let count = 1; // 3초마다 1씩 증가
- 0 :
- 1 :
- 2 : class="on"
event
1. DOMContentLoaded... 1번
2. init() 함수호출
init() 내부안에서
1. Element 내용들 가져오기
2. 함수선언 하기
- findIndex
- slide
/*
< slide >
count 변수에 있는 인덱스를 class="on" 달아주고, 이전에 달린 값을 class="" 로 만들어줌.
그리고 나서 count를 1씩 증가시킴.
*/
- btnHandler
- prevHandler
- nextHandler
3. 이벤트 등록
- Btns에 있는 모든 element에 대해 click 이벤트를 줌.
- prev, next element에게 click 이벤트를 줌.
4. slide() 호출
5. setInterval() 실행 - 비동기 [이벤트 루프]
- setInterval의 return 값을 intervalId 변수에 대입함.
- Background라는 공간에 3초마다 slide를 실행시킴.
사용자 클릭을 해야지만 실행됩니다.
6. btnHandler ...
목적 : 내가 클릭한 버튼이 어떤 버튼인지 알기 위함!!
고차 함수 : 해당 함수 안에서 함수를 return해주는 함수
function fn(index) {
return function (e) {
console.log(index, e)
}
}
Element.addEventListner("click", fn(1))
// +) fn()() >> 이 또한 고차함수와 같은 방식으로 결과값을 반환하는 방식이므로 기억해 둘 것!!(빈번히 사용한다고 함)
btns[0].onClick = function () {
//1
}
btns[1].onClick = function () {
//2
}
btns[2].onClick = function () {
//3
}
function handler() {
// 이것이 몇 번 버튼인지 알아야 함!!
}
btns[0].onClick = handler
btns[1].onClick = handler
btns[2].onClick = handler
0, 1, 2를 전달하고 싶은데 어떻게 전달할 수 있을까?
function handler(i) {
// 이것이 몇 번 버튼인지 알아야 함!!
return undefined; // 함수의 경우, return이 따로 명시되어 있지 않거나 return에 따로 값이나 계산식이 들어가지 않고 비어있는 경우에는 default 값으로 "undefined"를 반환함!!
}
btns[0].onClick = handler(0) // undefined
btns[1].onClick = handler(1) // undefined
btns[2].onClick = handler(2) // undefined
그렇다면 위의 함수를 실행해서 함수를 return 한다면 어떻게 될까?
function handler(index) {
// 이것이 몇 번 버튼인지 알아야 함!!
return function () {
console.log(index) // 오 이 버튼은 인덱스가 0번인 버튼이네!!
}
}
btns[0].onClick = handler(0) // function
btns[1].onClick = handler(1) // function
btns[2].onClick = handler(2) // function
내가 클릭한 버튼의 인덱스로 count 값이 바뀌길 원한다면 아래와 같이 코드를 작성함!!
imgs[index].className = "on"
문제 발생
기존에 있던 class="on"이 지워지지 않는 상황!!
첫번째 방법의 경우, 반복문을 통해서 문제를 해결함.
const arr = ["경일", "게임", "아카데미"]
const search = "게임"
// arr 배열 안에서 "게임" 뺴고 다 출력하기
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== search) {
console.log(arr[i])
}
}
우리가 슬라이드할 element들은 모두 배열에 담고 있었고.
우리는 버튼의 인덱스 번호도 알고 있음!!
function btnHandler(index) {
// 이것이 몇 번 버튼인지 알아야 함!!
return function () {
for (let i = 0; imgs.length; i++) {
if (i === index) {
imgs[i].clasName = "on"
} else {
imgs[i].className = "off"
}
}
}
}
반복문을 통해서 처리하기는 했지만 이 경우 반복문을 조금 돌렸지만 만일 내가 40개를 돌려야 되는 상황이라면, 너무 비효율적이다!!
내가 class="on"이 달려있는 index 번호를 알 수 있다면 이보다 더 쉽게 해결 가능하다!!
element.getAttribute()
<input type="text" />
const input = document.querySelector("type") // result : text
이 메서드를 활용하여 "findIndex()"라는 함수를 제작함!
const arr = ["경일", "게임", "아카데미"]
const search = "게임"
for (let i = 0; i < arr.length; i++) {
if (arr[i] === search) {
console.log(i)
}
}
function findIndex() {
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].getAttribute("class") === "on") return i
}
return -1
}
btnHandler의 비효율적인 문제를 위의 "findIndex" 함수 제작으로 해결함!!
function btnHandler(index) {
return function (e) {
const current = findIndex()
imgs[current].className = ""
imgs[index].className = "on"
count = index
}
}
btnHandler의 count 변수 재할당
3초마다
let count = 2 // 3초마다 증가. 0~2
2.9초 지났을 때 이전 인덱스에 대해 ""을 처리할 시 잘못된 인덱스를 바라보는 문제를 handler 함수의 "count = index" 코드로 해결함!!
- 0 : class="on"
- 1 :
- 2 :
'DOM' 카테고리의 다른 글
DOM 기초(7) - 숫자 증감기, 댓글(Create / Read) 구현 (0) | 2022.11.15 |
---|---|
DOM 기초(6) - 메뉴 / 서브메뉴, 이미지 슬라이드, 로또 (0) | 2022.11.14 |
DOM 기초(4) - setTimeout, setInterval, single thread, event loop (0) | 2022.11.10 |
DOM 기초(3) - DOMContentLoaded, Form 태그 이벤트, Element 생성 (0) | 2022.11.09 |
DOM 기초(2) - Event, 코드 실습(Lotto) (0) | 2022.11.08 |