* 연습과제 1
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>랜덤으로 타일의 색상 변경하기</h1>
<div id='tile-container'>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
<script src='app.js'></script>
</body>
</html>
body{
padding: 0;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
#tile-container{
width: 600px;
height: 600px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.tile{
width: 200px;
height: 200px;
box-sizing: border-box;
border: 1px solid skyblue;
}
const tiles = document.querySelectorAll('.tile')
console.log(tiles)
function getRandomColor(){
const red = Math.floor(Math.random() * 256)
const green = Math.floor(Math.random() * 256)
const blue = Math.floor(Math.random() * 256)
return `rgb(${red}, ${green}, ${blue})`
}
function changeBackground(e){
e.target.style.background = getRandomColor()
}
for(let tile of tiles){
console.log(tile)
tile.addEventListener('click', changeBackground)
}
* 연습과제 2
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="photo-container">
<img src="carousel-1.jpg" alt="">
</div>
<script src='app.js'></script>
</body>
</html>
body{
margin: 0;
padding: 0;
text-align: center;
}
.photo-container{
width: 500px;
height: 300px;
margin: 100px auto;
display: none;
}
.show{
display: block;
}
const photoContainer = document.querySelector('.photo-container')
function hideImg(){
photoContainer.classList.remove('show')
}
function showImg(){
photoContainer.classList.add('show')
setTimeout(hideImg, 3000)
}
function startEffect(){
setTimeout(showImg, 1000)
}
window.addEventListener('load', startEffect)
* 연습과제 3
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="count-box"></div>
<script src='app.js'></script>
</body>
</html>
body{
margin: 0;
padding: 0;
text-align: center;
}
#count-box{
width: 100%;
height: 100vh;
text-align: center;
font-size: 5rem;
font-weight: bold;
color: white;
background-color: thistle;
}
const countBox = document.getElementById('count-box')
let cnt = 0
function countNum() {
countBox.innerText = cnt
cnt++
}
setInterval(countNum, 1000);
* 연습과제 4
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id='text-box'>
<p></p>
</div>
<script src='app.js'></script>
</body>
</html>
body{
margin: 0;
padding: 0;
text-align: center;
}
#text-box{
width: 800px;
height: 100px;
line-height: 100px;
margin: 100px auto;
text-align: center;
font-size: 2rem;
font-weight: bold;
color: white;
background-color: thistle;
transition: all .5s;
}
const textBox = document.querySelector('#text-box p')
const text = 'You are watching text now !'
let index = 0
let timerID = null
function displayCharacter(){
console.log(index)
textBox.innerHTML += text[index]
index++
if(index > text.length - 1){
console.log('timer ended: ', index)
clearInterval(timerID)
}
}
function startTextAnimation(){
timerID = setInterval(displayCharacter, 500);
}
window.addEventListener('load', startTextAnimation)
* 연습과제 5
<!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>자바스크립트 연습</title>
<link rel='stylesheet' href='style.css'/>
</head>
<body>
<script src='app.js'></script>
</body>
</html>
.circle{
width: 100px;
height: 100px;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: peru;
background-image: url('super-mario-left.jpg');
background-repeat: no-repeat;
background-size: cover;
}
super-mario-left.jpg 파일 필요함
function addCircle(e){
const circle = document.createElement('circle')
circle.className = 'circle'
console.log(e.clientX, e.clientY)
const mouseX = e.clientX
const mouseY = e.clientY
circle.style.left = mouseX + 'px'
circle.style.top = mouseY + 'px'
document.body.appendChild(circle)
}
window.addEventListener('click', addCircle)
* 연습과제 6~10
아래 리스트 만들기 프로젝트의 코드 저장소를 참조해주세요!
style.css 파일과 app.js 파일에 주석으로 연습과제 번호를 달아두었으니 참고하기 바래요!
* 연습과제 11
<!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>자바스크립트 연습</title>
<link rel='stylesheet' href='style.css'/>
</head>
<body>
<button id='open-btn'>open sidebar</button>
<div class="sidebar">
<div class="menu">HOME</div>
<div class="menu">ABOUT</div>
<div class="menu">CONTACT</div>
<div class="menu">DETAIL</div>
<div class="menu">SETTING</div>
</div>
<script src='app.js'></script>
</body>
</html>
.sidebar{
width: 30%;
height: 100vh;
position: absolute;
left: -30%;
top: 0px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: all 0.7s ease-in-out;
}
.show{
left: 0px;
}
.menu{
width: 100%;
flex: 1;
background-color: peru;
font-size: 1.5rem;
display: flex;
justify-content: center;
align-items: center;
color: lightgoldenrodyellow;
border-bottom: 1px solid lightgoldenrodyellow;
}
.menu:hover{
color: peru;
background-color: lightgoldenrodyellow;
}
const openBtn = document.getElementById('open-btn')
const sidebar = document.querySelector('.sidebar')
let timerID = null
function hideSidebar(){
sidebar.classList.remove('show')
clearTimeout(timerID)
}
function openSidebar(){
sidebar.classList.add('show')
timerID = setTimeout(hideSidebar, 3000)
}
openBtn.addEventListener('click', openSidebar)
* 연습과제 12
최적화된 해답 - html, css 는 해답 0 과 동일함
const photos = document.getElementById('photos')
const selection = document.getElementById('selection')
const options = selection.querySelectorAll('.options')
const widthOfPhoto = 500 // 사진너비
let index = 0 // 사진과 셀렉터의 현재 위치를 나타내는 인덱스
let timerID = null // 타이머 ID
function changeIndicator(index){
selection.querySelector('.active').classList.remove('active') // 이전 active 한 셀렉터 제거
options[index].classList.add('active') // 현재 셀렉터에 active 추가
}
function changePosition(){
const photosLength = photos.querySelectorAll('.photo').length // 사진의 총 갯수
photos.style.marginLeft = (widthOfPhoto * index)*-1 + 'px' // -1 : 왼쪽으로 이동
changeIndicator(index) // 셀렉터 변경
index++
index = index > photosLength - 1 ? 0 : index
}
function startCarousel(){
timerID = setInterval(changePosition, 1000)
}
function stopCarousel(){
clearInterval(timerID)
}
function getIndexClicked(e){ // 클릭한 셀렉터의 인덱스값 구하기
if(e.target.className === 'options'){ // 이벤트 위임으로 셀렉터를 클릭한 경우에만 실행
for(let optionIndex in options){
if(options[optionIndex] === e.target){
console.log('clicked element', optionIndex)
index = optionIndex // 인덱스 업데이트
changePosition() // 클릭한 셀렉터 모양 변경 및 사진변경
}
}
}
}
photos.addEventListener('mouseenter', startCarousel)
photos.addEventListener('mouseleave', stopCarousel)
selection.addEventListener('click', getIndexClicked) // 추가된 부분
해답 0
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Carosel</h1>
<div id='photo-container'>
<div id="photos">
<div class='photo'></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
</div>
</div>
<div id='selection'>
<div class="options active"></div>
<div class="options"></div>
<div class="options"></div>
<div class="options"></div>
<div class="options"></div>
</div>
<script src='app.js'></script>
</body>
</html>
options 에 id 를 사용하지 않고 구현하는 방법은 위와 같다.
body{
margin: 0;
padding: 0;
text-align: center;
}
#photo-container{
width: 500px;
height: 250px;
margin: 100px auto;
overflow: hidden;
}
#photos{
width: 2500px;
height: 100%;
cursor: pointer;
display: flex;
transition: all 1s;
/* margin-left: -500px; */
}
.photo{
width: 100%;
height: 100%;
overflow: hidden;
background-size: cover;
}
.photo:nth-child(1) {
background: url("https://media.istockphoto.com/photos/forest-wooden-table-background-summer-sunny-meadow-with-green-grass-picture-id1353553203?b=1&k=20&m=1353553203&s=170667a&w=0&h=QTyTGI9tWQluIlkmwW0s7Q4z7R_IT8egpzzHjW3cSas=") no-repeat center;
}
.photo:nth-child(2) {
background: url("https://prod-virtuoso.dotcmscloud.com/dA/188da7ea-f44f-4b9c-92f9-6a65064021c1/previewImage/PowerfulReasons_hero.jpg") no-repeat center;
}
.photo:nth-child(3) {
background: url("http://file.instiz.net/data/file/20121125/5/6/0/5609249762358c2bcb65a46580549c99") no-repeat center;
}
.photo:nth-child(4) {
background: url("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSN_Dap1JjDe2mQpi2FeEfC-pIXJmRbpYHmug&usqp=CAU") no-repeat center;
}
.photo:nth-child(5){
background: url('https://www.institutostrom.org/wp-content/uploads/2018/04/NZ.jpg') no-repeat center;
}
/* 커로셀 셀렉터 */
#selection{
width: 300px;
height: 50px;
margin: 0px auto;
display: flex;
justify-content: center;
align-items: center;
}
.options{
width: 20px;
height: 20px;
border-radius: 50%;
margin-left: 10px;
background-color: lightgreen;
box-shadow: 1px 1px 5px 2px lightgreen;
user-select: none; /* 커서 제거 */
transition: all 0.7s;
}
.options:hover{
background-color: greenyellow;
cursor: pointer;
}
.options.active{
width: 50px;
}
const photos = document.getElementById('photos')
const selection = document.getElementById('selection') // 추가된 부분
const widthOfPhoto = 500 // 사진너비
let marginLeft = widthOfPhoto // 사진이동 간격
let timerID = null // 타이머 ID
function changeIndicator(index){
const selection = document.getElementById('selection')
const activeOption = selection.querySelector('.active')
if(activeOption) activeOption.classList.remove('active') // 이전 active 한 인디케이터 제거
selection.querySelectorAll('.options')[index].classList.add('active') // 현재 인디케이터에 active 추가
}
function changePosition(){
const photosLength = photos.querySelectorAll('.photo').length // 사진의 총 갯수
photos.style.marginLeft = marginLeft*-1 + 'px' // -1 : 왼쪽으로 이동
const index = parseInt(marginLeft / widthOfPhoto) // 현재 사진의 인덱스값
changeIndicator(index) // 인디케이터 변경
marginLeft = marginLeft >= (widthOfPhoto * (photosLength - 1)) ? 0 : marginLeft + widthOfPhoto // 사진 너비만큼 이동
}
function startCarousel(){
timerID = setInterval(changePosition, 1000)
}
function stopCarousel(){
clearInterval(timerID)
}
function getIndexClicked(e){ // 클릭한 셀렉터의 인덱스값 구하기
if(e.target.className === 'options'){
const options = selection.querySelectorAll('#selection .options')
for(let index in options){
if(options[index] === e.target){
console.log('clicked element', index)
marginLeft = widthOfPhoto * index // 사진이동 간격 설정하기
changePosition() // 클릭한 셀렉터 모양 변경 및 사진변경
}
}
}
}
photos.addEventListener('mouseenter', startCarousel)
photos.addEventListener('mouseleave', stopCarousel)
selection.addEventListener('click', getIndexClicked) // 추가된 부분
해답 1
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Carosel</h1>
<div id='photo-container'>
<div id="photos">
<div class='photo'></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
</div>
</div>
<div id='selection'>
<div class="options active" id="0"></div>
<div class="options" id="1"></div>
<div class="options" id="2"></div>
<div class="options" id="3"></div>
<div class="options" id="4"></div>
</div>
<script src='app.js'></script>
</body>
</html>
body{
margin: 0;
padding: 0;
text-align: center;
}
#photo-container{
width: 500px;
height: 250px;
margin: 100px auto;
overflow: hidden;
}
#photos{
width: 2500px;
height: 100%;
cursor: pointer;
display: flex;
transition: all 1s;
/* margin-left: -500px; */
}
.photo{
width: 100%;
height: 100%;
overflow: hidden;
background-size: cover;
}
.photo:nth-child(1) {
background: url("https://media.istockphoto.com/photos/forest-wooden-table-background-summer-sunny-meadow-with-green-grass-picture-id1353553203?b=1&k=20&m=1353553203&s=170667a&w=0&h=QTyTGI9tWQluIlkmwW0s7Q4z7R_IT8egpzzHjW3cSas=") no-repeat center;
}
.photo:nth-child(2) {
background: url("https://prod-virtuoso.dotcmscloud.com/dA/188da7ea-f44f-4b9c-92f9-6a65064021c1/previewImage/PowerfulReasons_hero.jpg") no-repeat center;
}
.photo:nth-child(3) {
background: url("http://file.instiz.net/data/file/20121125/5/6/0/5609249762358c2bcb65a46580549c99") no-repeat center;
}
.photo:nth-child(4) {
background: url("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSN_Dap1JjDe2mQpi2FeEfC-pIXJmRbpYHmug&usqp=CAU") no-repeat center;
}
.photo:nth-child(5){
background: url('https://www.institutostrom.org/wp-content/uploads/2018/04/NZ.jpg') no-repeat center;
}
/* 커로셀 셀렉터 */
#selection{
width: 300px;
height: 50px;
margin: 0px auto;
display: flex;
justify-content: center;
align-items: center;
}
.options{
width: 20px;
height: 20px;
border-radius: 50%;
margin-left: 10px;
background-color: lightgreen;
box-shadow: 1px 1px 5px 2px lightgreen;
user-select: none; /* 커서 제거 */
transition: all 0.7s;
}
.options:hover{
background-color: greenyellow;
cursor: pointer;
}
.options.active{
width: 50px;
}
const photos = document.getElementById('photos')
const selection = document.getElementById('selection') // changeIndicator 함수 내부로부터 이동한 부분
const widthOfPhoto = 500 // 사진너비
let marginLeft = widthOfPhoto // 사진이동 간격
let timerID = null // 타이머 ID
function changeIndicator(index){
const activeOption = selection.querySelector('.active')
if(activeOption) activeOption.classList.remove('active') // 이전 active 한 인디케이터 제거
selection.querySelectorAll('.options')[index].classList.add('active') // 현재 인디케이터에 active 추가
}
function changePosition(){
const photosLength = photos.querySelectorAll('.photo').length // 사진의 총 갯수
photos.style.marginLeft = marginLeft*-1 + 'px' // -1 : 왼쪽으로 이동
const index = parseInt(marginLeft / widthOfPhoto) // 현재 사진의 인덱스값
changeIndicator(index) // 인디케이터 변경
marginLeft = marginLeft >= (widthOfPhoto * (photosLength - 1)) ? 0 : marginLeft + widthOfPhoto // 사진 너비만큼 이동
}
function changePhoto(e){ // 추가된 부분
if(e.target.className === 'options'){
marginLeft = parseInt(e.target.id) * widthOfPhoto
changePosition()
}
}
function startCarousel(){
timerID = setInterval(changePosition, 1000)
}
function stopCarousel(){
clearInterval(timerID)
}
photos.addEventListener('mouseenter', startCarousel)
photos.addEventListener('mouseleave', stopCarousel)
selection.addEventListener('click', changePhoto) // 추가된 부분
* 연습과제 13
const boxes = document.querySelectorAll('.box')
let select = 0
let prevSelect = 0
function moveBox(){
const prevBox = boxes[prevSelect]
const box = boxes[select]
prevBox.style.marginBottom = 0 + 'px'
box.style.marginBottom = 50 + 'px'
prevSelect = select
select++
if(select > boxes.length - 1){
select = 0
}
}
function startMove(){
setInterval(moveBox, 500)
}
window.addEventListener('load', startMove)
* 연습과제 14
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="menu-container">
<div class="menu">
<div class='title'>사과</div>
<div class='info'>
<h4>단가 : 700원</h4>
<h4>색깔 : 빨간색</h4>
</div>
</div>
<div class="menu">
<div class='title'>바나나</div>
<div class='info'>
<h4>단가 : 200원</h4>
<h4>색깔 : 노란색</h4>
</div>
</div>
<div class="menu">
<div class='title'>오렌지</div>
<div class='info'>
<h4>단가 : 1000원</h4>
<h4>색깔 : 주황색</h4>
</div>
</div>
</div>
<script src='app.js'></script>
</body>
</html>
body{
margin: 0;
padding: 0;
}
#menu-container{
width: 500px;
margin: 100px auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.menu{
width: 100%;
flex: 1;
margin-bottom: 10px;
cursor: pointer;
user-select: none;
}
.title{
background: lightgray;
padding: 10px;
}
.info{
padding: 10px;
background: lightgoldenrodyellow;
color: goldenrod;
font-weight: bold;
display: none;
transition: all 0.5s;
}
.open{
display: block;
}
const menuContainer = document.getElementById('menu-container')
let prevTarget = null
function openMenu(e){
const target = e.target
if(prevTarget !== null){
prevTarget.nextElementSibling.classList.remove('open')
}
if(target.className === 'title'){
console.dir(target)
target.nextElementSibling.classList.add('open')
prevTarget = target
}
}
menuContainer.addEventListener('click', openMenu)
위와 같이 작성하면 하나의 아이템 정보만 열리기는 하지만 열려있던 아이템 정보를 보이지 않게 하지는 못한다. 열려있던 아이템 정보를 다시 클릭해서 숨기려면 아래와 같이 자바스크립트 코드를 수정하면 된다.
const menuContainer = document.getElementById('menu-container')
let prevTarget = null
function openMenu(e){
const prevInfo = document.querySelector('.open')
if(prevInfo) prevInfo.classList.remove('open')
if(e.target.classList.contains('title') && e.target.nextElementSibling !== prevInfo){
e.target.nextElementSibling.classList.toggle('open')
}
}
menuContainer.addEventListener('click', openMenu)
하지만 이전에 열려있던 아이템과 현재 클릭한 아이템이 동일하면 classList 의 remove 메서드 때문에 아이템 정보가 숨겨졌다가 toggle 에 의해서 다시 열린다. 즉, toggle 메서드는 이전의 아이템과 현재 아이템이 다른 경우에만 적용해야 한다. 그렇게 해야 이전에 열려있던 아이템 정보를 닫고, 현재 아이템 정보는 토글해서 열린다.
연습과제 15
<!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>자바스크립트 연습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id='contents'></div>
<div id="page-btns"></div>
<script src='app.js'></script>
</body>
</html>
body{
margin: 0;
padding: 0;
text-align: center;
}
#contents{
width: 100%;
min-height: 100vh;
background-color: thistle;
color: white;
font-weight: bold;
/* padding: 15px; */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.list-item{
width: 500px;
height: 100px;
background-color: white;
color: purple;
margin: 10px;
}
#page-btns{
position: fixed;
bottom: 50px;
left: 50%;
transform: translate(-50%);
}
.page-btn{
width: 50px;
height: 50px;
background-color: purple;
border-radius: 10px;
margin-right: 5px;
cursor: pointer;
color: white;
font-size: 1rem;
font-weight: bold;
border: none;
outline: none;
}
const pageContents = [] // 데이터를 담고 있는 배열
const numOfData = 120 // 전체 리스트 수
const limit = 4 // 페이지당 보여줄 리스트 수
const numOfBtns = 5 // 페이지당 보여줄 버튼 수
const totalNumOfBtns = Math.ceil(numOfData/limit) // 총 버튼수
let offset = 0, btnOffset = 0
const contents = document.getElementById('contents')
const pageBtns = document.getElementById('page-btns')
// 버튼 생성하기
function buildBtn(contents){
const btn = document.createElement('button')
btn.className = 'page-btn'
btn.innerText = contents
return btn
}
// 화면에 페이지네이션 버튼 보여주기
function showPaginationBtns(pageBtns, btnOffset, numOfBtns){
pageBtns.innerHTML = ''
if(btnOffset > 0){
pageBtns.appendChild(buildBtn("<"))
}
for(let i=btnOffset;i<btnOffset+numOfBtns;i++){
if(i >= 0 && i < totalNumOfBtns){
pageBtns.appendChild(buildBtn(i+1))
}
}
if(btnOffset + numOfBtns < totalNumOfBtns){
console.log(btnOffset, numOfBtns, totalNumOfBtns)
console.log(btnOffset, numOfBtns)
pageBtns.appendChild(buildBtn(">"))
}
}
function showList(pageContents, offset, limit, contents){
contents.innerHTML = '' // 화면 초기화
for(let i=offset; i<offset+limit; i++){
const listItem = pageContents[i]
if(listItem){
contents.innerHTML += `
<div id=${listItem.id} class='list-item'>
<h3>${listItem.name} (${listItem.id})</h3>
<h3>${listItem.age}</h3>
</div>
`
}
}
}
// 리스트 배열 만들기
for(let i=0;i<numOfData;i++){
pageContents.push({ name: 'sunrise', age: 20, id: i })
}
console.log(pageContents)
function changePage(e){
const target = e.target
if(target.className === 'page-btn'){
if(target.innerText === '>'){
btnOffset += numOfBtns
showPaginationBtns(pageBtns, btnOffset, numOfBtns)
}else if(target.innerText === '<'){
btnOffset -= numOfBtns
showPaginationBtns(pageBtns, btnOffset, numOfBtns)
}else{
const indexSelected = parseInt(target.innerText) - 1
offset = limit * indexSelected // 콘텐츠 시작점(오프셋) = (페이지번호 - 1) x 페이지당 보여줄 갯수
showList(pageContents, offset, limit, contents) // 클릭한 페이지 로딩
}
}
}
showList(pageContents, offset, limit, contents)
showPaginationBtns(pageBtns, btnOffset, numOfBtns)
pageBtns.addEventListener('click', changePage)
const numOfBtns = 5 // 페이지당 보여줄 버튼 수
const totalNumOfBtns = Math.ceil(numOfData/limit) // 총 버튼수
let offset = 0, btnOffset = 0
페이지당 보여줄 버튼수는 이전/다음 버튼을 누를때마다 생성할 버튼의 갯수이다. btnOffset 은 페이지가 전환될때마다 보여줄 버튼의 시작번호이다.
// 버튼 생성하기
function buildBtn(contents){
const btn = document.createElement('button')
btn.className = 'page-btn'
btn.innerText = contents
return btn
}
버튼을 생성하는 부분을 함수로 정의하였다.
// 화면에 페이지네이션 버튼 보여주기
function showPaginationBtns(pageBtns, btnOffset, numOfBtns){
pageBtns.innerHTML = ''
if(btnOffset > 0){
pageBtns.appendChild(buildBtn("<"))
}
for(let i=btnOffset;i<btnOffset+numOfBtns;i++){
if(i >= 0 && i < totalNumOfBtns){
pageBtns.appendChild(buildBtn(i+1))
}
}
if(btnOffset + numOfBtns < totalNumOfBtns){
console.log(btnOffset, numOfBtns, totalNumOfBtns)
console.log(btnOffset, numOfBtns)
pageBtns.appendChild(buildBtn(">"))
}
}
화면에 페이지네이션 버튼을 보여주는 함수이다. 이때 btnOffset 이 제로(0)보다 큰 경우에만 이전 버튼을 보여준다. 초기 로딩시에 btnOffset 은 제로(0)이므로 이전 버튼은 숨긴다.
해당 페이지에 보여줄 버튼은 시작점(btnOffset)부터 끝점(btnOffset+numOfBtns)까지 버튼을 생성하고 화면에 보여준다. 이때 해당 페이지에 보여줄 버튼이 numOfBtns (5) 보다 적은 경우에는 numOfBtns (5)만큼 생성하면 안된다. 그러므로 조건문을 이용하여 페이지네이션 버튼의 숫자가 1 이상이고 totalNumOfBtns (페이지수) 이하인 경우에만 버튼을 생성하도록 한다.
만약 마지막 버튼의 숫자(btnOffset+numOfBtns)가 totalNumOfBtns (페이지수) 보다 커지면 더이상 다음 페이지는 없으므로 조건문을 이용하여 다음 버튼은 생성하지 않도록 한다.
function showList(pageContents, offset, limit, contents){
contents.innerHTML = '' // 화면 초기화
for(let i=offset; i<offset+limit; i++){
const listItem = pageContents[i]
if(listItem){
contents.innerHTML += `
<div id=${listItem.id} class='list-item'>
<h3>${listItem.name} (${listItem.id})</h3>
<h3>${listItem.age}</h3>
</div>
`
}
}
}
해당 페이지의 내용을 보여주는 부분이다. 리스트 아이템의 시작점(offset)부터 끝점(offset+limit)까지 리스트 아이템 정보를 HTML 코드로 변경하고 화면에 보여준다.
페이지당 보여줄 리스트 아이템의 갯수는 limit 이지만 마지막 페이지에는 보여줄 갯수가 limit 보다 적은 경우가 있으므로 이때는 조건문을 이용하여 listItem 이 존재하는 경우에만 해당 아이템을 contents 에 추가할 수 있도록 한다.
showList(pageContents, offset, limit, contents)
showPaginationBtns(pageBtns, btnOffset, numOfBtns)
웹 화면 초기 로딩시에 리스트와 페이지네이션 버튼을 보여주기 위하여 해당 함수들을 실행한다.
// 리스트 배열 만들기
for(let i=0;i<numOfData;i++){
pageContents.push({ name: 'sunrise', age: 20, id: i })
}
console.log(pageContents)
실무에서는 리스트 데이터를 서버에서 조회해서 가져오지만 현재는 서버가 없으므로 가상으로 데이터를 생성한다.
function changePage(e){
const target = e.target
if(target.className === 'page-btn'){
if(target.innerText === '>'){
btnOffset += numOfBtns
showPaginationBtns(pageBtns, btnOffset, numOfBtns)
}else if(target.innerText === '<'){
btnOffset -= numOfBtns
showPaginationBtns(pageBtns, btnOffset, numOfBtns)
}else{
const indexSelected = parseInt(target.innerText) - 1
offset = limit * indexSelected // 콘텐츠 시작점(오프셋) = (페이지번호 - 1) x 페이지당 보여줄 갯수
showList(pageContents, offset, limit, contents) // 클릭한 페이지 로딩
}
}
}
다음 버튼을 누르면 btnOffset 이 numOfBtns (5)만큼 증가하여 다음 페이지네이션 버튼 세트를 보여준다. 이전 버튼을 누르면 btnOffset 이 numOfBtns (5)만큼 감소하여 이전 페이지네이션 버튼 세트를 보여준다. 페이지네이션 버튼을 클릭하면 해당 페이지의 리스트 목록을 보여준다.
'프론트엔드 > Javascript 연습과제 해답' 카테고리의 다른 글
자바스크립트 문법 5 - 이벤트(Event) 처리하기 3 해답 (0) | 2022.01.15 |
---|---|
자바스크립트 문법 4 - 이벤트 (Event) 처리하기 2 해답 (0) | 2022.01.15 |
자바스크립트 문법 6 - 배열 (Array) 의 기본 해답 (0) | 2022.01.14 |
자바스크리트 문법 10 - 2차원 배열과 배열의 확장 해답 (0) | 2022.01.14 |
자바스크립트 문법 9 - 배열(Array) 의 다양한 메서드 3 (분해와 융합) 해답 (2) | 2022.01.14 |