바닐라 JS를 이용해서 간단한 ToDo를 입력 받을 수 있는 캘린더를 만들어보자 다짐했었다.
지난 포스팅에서는 내가 직접 디자인 한 UI를 구현했고, 오늘은 그 안의 내용을 채워줄 JavaScript 로직을 뜯어볼 것이다.
크게 캘린더 부분의 날짜를 불러오고 내가 원하는 날짜로 이동하는 로직과 지정한 날짜에 ToDo List를 입력하고 저장하는 로직으로 나뉠 것이다.
UI를 어떻게 구현했는지 궁금하다면
이번 달 날짜 불러오기
이번 달의 날짜들을 불러오기 위해서는 어떤 정보들이 필요할까?
- 이번 달이 몇 년도 몇 월인지
- 이번 달이 몇 일까지 있는지, 마지막 일이 무슨 요일인지
- 앞 뒤로 달력에 포함될 전 월의 마지막 일자들과 후 월의 앞부분 일자
당장의 캘린더 한 장을 채우기 위한 정보는 이 정도만 있으면 될 것 같다.
그럼 바로 코드로 작성해보자!
// 오늘 날짜로 Date 객체 생성하기
const date = new Date();
// 오늘 날짜의 해와 월, 일 구하기
const currentYear = date.getFullYear();
const currentMonth = date.getMonth();
const day = date.getDate();
// year-month 태그 안에 구한 년과 월을 띄운다.
// 이번 달을 구하기 위해서는 +1 을 해주어야 한다.
document.querySelector(".year-month").textContent = `${currentYear}년 ${
currentMonth + 1
}월`;
// 이전 달의 마지막 Date와 현재 월의 마지막 Date를 구한다. (0을 주면 마지막 일로 생성)
// Tue Apr 30 2024 00:00:00 GMT+0900 (한국 표준시) 형식으로 반환 됨
const prevLastDate = new Date(currentYear, currentMonth, 0);
const currentLastDate = new Date(currentYear, currentMonth + 1, 0);
// 각 각의 Date에서 일자만 뽑아낸다.
// 30 형식으로
const prevLast = prevLastDate.getDate();
const prevLastDay = prevLastDate.getDay();
// 현재 달의 마지막 일자와 요일을 구한다.
const currentLast = currentLastDate.getDate();
const currentLastDay = currentLastDate.getDay();
const prevDates = [];
// 이번 달의 Date 객체의 일자만 뽑아내어 배열에 넣고, slice(1)하여 한 날짜씩 끊는다.
/** 사실상 이 로직과 동일한 과정
thisDates -> for (let i = 1; i <= currentLast; i++) {
prevDates.push(i);
} */
const thisDates = [...Array(currentLast + 1).keys()].slice(1);
const nextDates = [];
// 전 달의 마지막 일자가 토요일이 아니면, (토요일이면 들어갈 자리 없음, 바로 이번 달 시작하면 됨)
// prevDates배열에 마지막 요일 - i 만큼 뺀 값을 앞에 추가한다.
// 전 달의 마지막 요일이 수요일이면 4이면 4개의 일자가 붙어야 함
if (prevLastDay !== 6) {
for (let i = 0; i < prevLastDay + 1; i++) {
prevDates.unshift(prevLast - i);
}
}
// 이번달의 요일을 7에서부터 빼서 다음 달의 초기 일자를 구한다.
for (let i = 1; i < 7 - currentLastDay; i++) {
nextDates.push(i);
}
// 한 달력에 들어갈 요일들을 concat으로 합친다.
const dates = prevDates.concat(thisDates, nextDates);
// 그냥 반복문으로 쭉 넣어줘도 되지만,
// 이전 달 및 이후 달의 일자는 회색으로 표시되길 원해서 조건을 걸어주었다.
dates.forEach((date, i) => {
if (i < prevDates.length) {
dates[i] = `<div class="date prev">${date}</div>`;
} else if (i > prevDates.length + thisDates.length) {
dates[i] = `<div class="date prev">${date}</div>`;
} else if (prevDates.length < i < dates.length - nextDates.length) {
dates[i] = `<div class="date">${date}</div>`;
}
});
// dates태그 안에 dates의 배열들을 하나씩 끊어서 넣어준다.
document.querySelector(".dates").innerHTML = dates.join("");
이렇게 코드를 작성하면 아래와 같이 잘 들어가는 것을 볼 수 있다.
하지만 여기서 만족할 수는 없다.
나는 상단의 화살표를 누르면 다음 달로, 이전 달로 마구마구 이동하는 자유로운 캘린더를 만들고 싶다.
코드를 짜기 전에 생각해보자. 어떻게 해줄 수 있을까?
이전 달, 이후 달 캘린더 확인하기
가장 먼저 든 생각은 화살표를 누를 때마다 현재 년도, 월의 값을 바꿔주면 될 것 같다.
이번에도 코드로 보자
...// /** 이전 로직 */
// change selectMonth
document.addEventListener("DOMContentLoaded", function () {
const prevButton = document.querySelector(".nav-button.go-prev");
const nextButton = document.querySelector(".nav-button.go-next");
function handleSelectMonth(event) {
/** 버튼이 이전 달로 가는 버튼인지 아닌지에 따라 다른 로직이 동작 */
if (event.target.classList.contains("go-prev")) {
/** 1월에서 이전 달로 가기 버튼을 누르면 년도가 이전 년도로 업데이트 */
/** 12월에서 이후 달로 가기 버튼을 누르면 년도가 이후 년도로 업데이트 */
if (selectMonth === 0) {
selectYear--;
selectMonth = 11;
} else {
selectMonth--;
}
} else if (event.target.classList.contains("go-next")) {
if (selectMonth === 11) {
selectYear++;
selectMonth = 0;
} else {
selectMonth++;
}
}
/** 캘린더의 날짜를 업데이트하는 로직 **/
const prevLastDate = new Date(selectYear, selectMonth, 0);
const currentLastDate = new Date(selectYear, selectMonth + 1, 0);
const prevLast = prevLastDate.getDate();
const prevLastDay = prevLastDate.getDay();
const currentLast = currentLastDate.getDate();
const currentLastDay = currentLastDate.getDay();
const prevDates = [];
const thisDates = [...Array(currentLast + 1).keys()].slice(1);
const nextDates = [];
if (prevLastDay !== 6) {
for (let i = 0; i < prevLastDay + 1; i++) {
prevDates.unshift(prevLast - i);
}
}
for (let i = 1; i < 7 - currentLastDay; i++) {
nextDates.push(i);
}
dates = prevDates.concat(thisDates, nextDates);
dates.forEach((date, i) => {
if (i < prevDates.length) {
dates[i] = `<div class="date prev">${date}</div>`;
} else if (i > prevDates.length + thisDates.length - 1) {
dates[i] = `<div class="date prev">${date}</div>`;
} else if (prevDates.length < i < dates.length - nextDates.length) {
dates[i] = `<div class="date">${date}</div>`;
}
});
document.querySelector(".dates").innerHTML = dates.join("");
document.querySelector(".today").textContent = `${selectYear}년 ${
selectMonth + 1
}월`;
}
/** 버튼에 이벤트 등록 */
if (prevButton) {
prevButton.addEventListener("click", handleSelectMonth);
}
if (nextButton) {
nextButton.addEventListener("click", handleSelectMonth);
}
});
selectMonth와 selectYear을 바꿔줌과 동시에, 바꿔준 값에 맞는 날짜들로 다시 업데이트 해줘야하기 때문에 handler함수 안에서 또 다시 캘린더의 날짜를 채워주는 로직들이 반복된다.
이후, 추상화 과정을 통해서 반복되는 로직들을 줄이는 시간을 가질 예정이다.
오늘의 목표였던 캘린더 월별 전환까지 구현해보았다.
한 달의 그리드가 다섯 줄인 달과 여섯 줄인 달이 있어, 레이아웃 전체의 높이가 들쑥날쑥해지는 문제가 있었다.
그 부분을 개선하기 위해 CSS를 살짝 수정했다.
-> 이전 포스팅의 CSS 부분에 반영할 예정 - !
오늘의 결과물을 확인해보자~!
다음 구현 목표
- 특정 날짜 선택하기
- 선택된 날짜에 Todo 등록하기 (로컬 스토리지 이용 예정)
오늘은 여기까지 ~!
그럼 이만 !
'💡뚝딱뚝딱 만들어보자 ~! :) > [Vanila JS] Calender Todo' 카테고리의 다른 글
[Vanila JS] Calender Todo - UI (0) | 2024.05.17 |
---|