<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>다음 달로 넘어가는 30일 범위 클릭 가능한 달력</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
text-align: center;
padding: 20px;
height: 100vh;
display: flex;
justify-content: center;
flex-direction: column;
}
table {
margin: 0 auto;
border-collapse: collapse;
width: 80vh;
height: 70vh;
box-shadow: 0 3px 5px rgba(0.5, 0.5, 0.5, 0.5);
}
th, td {
border: 1px solid #ddd;
padding: 10px;
text-align: center;
}
th {
background-color: tomato;
color: white;
position: relative;
}
button {
background-color: #fff;
border: none;
width: 100%;
height: 100%;
cursor: pointer;
background-color: transparent;
}
button.disabled {
background-color: #f0f0f0;
color: #999;
cursor: not-allowed;
}
button.disabled:hover {
background-color: #f0f0f0;
transform: none;
color: #999;
}
button.enabled:hover {
background-color: #e7e7e7;
transform: scale(1.3);
color: red;
}
.today {
background-color: lightblue;
}
.nav-button {
width: 70px;
height: 50px;
margin: 10px;
padding: 10px;
background-color: tomato;
color: white;
border: none;
cursor: pointer;
}
.nav-button:hover {
background-color: #ff7f7f;
}
#prev-btn{
width: 30px;
height: 30px;
position: absolute;
top: 20.7%;
left: 43%;
border: 1px solid white;
border-radius: 50px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
#next-btn{
position: absolute;
width: 30px;
height: 30px;
top: 20.7%;
right:43%;
border: 1px solid white;
border-radius: 50px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
span{
display: block;
margin-bottom: 5px;
font-size: 1.4em;
}
#next-btn span{
margin-top: -10px;
margin-left: -3px;
}
#prev-btn span{
margin-top: -10px;
margin-left: -6px;
}
.today{
color: red !important;
}
</style>
</head>
<body>
<div id="calendar">
</div>
<button class="nav-button" id="prev-btn" onclick="changeMonth(-1)">
<span>◁</span>
</button>
<button class="nav-button" id="next-btn" onclick="changeMonth(1)">
<span>▷</span>
</button>
<script>
let currentMonthOffset = 0; // 현재 달을 기준으로 얼마나 이동했는 지 추적
let MaxMonth = 2;
function createCalendar() {
const today = new Date(); // Date 객체 생성 : 현재 날짜와 시간을 가져온다.
/* 1. 현재 월을 기준으로 */
today.setMonth(today.getMonth() + currentMonthOffset);
const currentMonth = today.getMonth();
const year = today.getFullYear(); // 년도 4자리를 호출하는 메소드이다
const currentDay = new Date().getDate();
/*현재 월을 기준으로 */
/*today가 있는데 currentDay를 new Date().getDate(); 구하는 이유?? */
// const today = new Date();로 생성된 Date 객체는 현재 날짜와 시간은을 포함한다.
// new Date().getDate();로 얻은 값으로, 현재 월의 몇 번째 날인지를 나타낸다.
/*현재 달의 첫 번째 */
const firstDay = new Date(year, currentMonth, 1);
// 특정 연도의 첫번째 날을 나타낸다. 맨 뒤 1은
// 지정된 연도와 지정된 날의 1일을 의미한다.
/*현재 달의 첫 번째 */
/*다음달의 마지막 날을 구하는 코드*/
const lastDay = new Date(year, currentMonth + 1, 0);
// currentMonth + 1 현재 월에 1을 더한 값으로 다음달을 나타낸다.
// 날짜를 0으로 설정하면, 해당 월의 마지막 날을 반환한다.
/*다음달의 마지막 날을 구하는 코드*/
/*
현재 달의 첫 번째 날과 다음 달의 마지막 날을 알고 있으면
빈칸을 추가하고 날짜를 정확한 위치에 표시할 수 있어서 달력이 보기 좋게 된다
*/
const daysInMonth = lastDay.getDate();
// 다음 달의 마지막 날짜를 생성한다.
// 10월이 31일이라면 31일을 반환한다.
// 오늘 기준으로 30일 뒤 계산
const startDate = new Date(); // 오늘 날짜 기준
const futureDate = new Date();
futureDate.setDate(startDate.getDate() + 30); // 30일 뒤
let calendarHtml = '';
calendarHtml += '<table>'
+ '<tr><th colspan="7">' + year + '년 ' + (currentMonth + 1) + '월</th></tr>'
+ '<tr><th>일</th><th>월</th><th>화</th><th>수</th><th>목</th><th>금</th><th>토</th></tr>'
+ '<tr>';
const firstDayOfWeek = firstDay.getDay();
for (let i = 0; i < firstDayOfWeek; i++) {
calendarHtml += '<td></td>';
}
// getDay() 메소드는 해당 날짜가 주의 몇 번쨰 요일인지를 반환해준다.
// 0 일요일 6 토요일
// 날짜 추가
for (let day = 1; day <= daysInMonth; day++) {
if ((day + firstDayOfWeek - 1) % 7 === 0 && day !== 1) {
calendarHtml += '</tr><tr>'; // 줄바꿈
}
// day + firstDayOfWeek - 1 현재 날짜day와 첫 번째 날의 요일
// firstDayOfWeek을 더 한 후 1을 빼서 전체 날짜를 계산한다.
// 한 주의 마지막 날짜인 경우(일요일)인 경우 줄바꿈을 해준다.
const thisDate = new Date(year, currentMonth, day);
let isClickable = thisDate >= startDate && thisDate <= futureDate;
// 날짜 클릭 가능 여부 thisDate가 오늘 날짜와 30일 후 사이에 있는 지 여부를 확인한다.
let classList = "";
// 오늘 날짜 강조
if (thisDate.toDateString() === new Date().toDateString()) {
classList += "today";
}
// toDateString() => thisDate 객체를 문자열 형태로 변환하여 날짜와
// 시간을 제외한 "YYYY-MM-DD" 형식으로 반환한다.
// 버튼 생성
if (isClickable) {
calendarHtml += `
<td>
<button class="enabled ${classList}" onclick="dateClicked(${day})">${day}</button>
</td>
`;
// enabled 주로 사용자 인터페이스에서 버튼이나 입력 필드가 활성화되어 사용자가
// 상호작용할 수 있는 상태를 의미한다.
} else {
calendarHtml += `
<td>
<button class="disabled ${classList}" disabled>${day}</button>
</td>
`;
}
}
calendarHtml += '</tr></table>';
document.getElementById('calendar').innerHTML = calendarHtml;
// 다음 버튼 처리
if (currentMonthOffset + 1< MaxMonth) {
document.getElementById('next-btn').style.display = 'block';
} else {
document.getElementById('next-btn').style.display = 'none';
}
// 이전 버튼 처리
if (currentMonthOffset - 2 > -MaxMonth) {
document.getElementById('prev-btn').style.display = 'block'; // 이전 버튼 보이기
} else {
document.getElementById('prev-btn').style.display = 'none'; // 이전 버튼 숨기기
}
}
function dateClicked(day) {
const today = new Date();
const month = today.getMonth() + 1 + currentMonthOffset; // 현재 월 + 오프셋
const year = today.getFullYear();
confirm(`선택한 날짜: ${year}년 ${month}월 ${day}일로 예약 진행하겠습니까?`);
}
function changeMonth(offset) {
if (currentMonthOffset + offset < MaxMonth && currentMonthOffset + offset > -MaxMonth) {
currentMonthOffset += offset;
}
createCalendar();
}
createCalendar();
</script>
</body>
</html>