달력 생성 함수(calendar.js)를 사용하여 캘린더(Calendar)를 만들겠습니다.
캘린더 UI 구성
기존 달력 UI에 연간과 월간 달력으로 변경할 수 있게 달력 뷰 버튼 그룹을 추가합니다.
<div class="calendar">
<div class="calendar-header">
<span class="calendar-yearmonth"></span>
<div class="calendar-controls">
<button type="button" class="calendar-prev"><</button>
<button type="button" class="calendar-next">></button>
<button type="button" class="calendar-today">오늘</button>
</div>
<div class="calendar-views">
<button type="button" class="calendar-view-year">년간</button>
<button type="button" class="calendar-view-month">월간</button>
</div>
</div>
<div id="calendar"></div>
</div>
기존 style에 달력 컨트롤 버튼 그룹(.calendar-controls)을 수정하고 달력 뷰 버튼 그룹(.calendar-views)를 추가합니다.
년과 월을 표시하는 <span> 태그와 달력 컨트롤 버튼 그룹(.calendar-controls)인 <div> 태그가 연속으로 나열되기 위해서 display를 inline-block으로 변경하고 달력 컨트롤 버튼 그룹(.calendar-controls)인 <div> 태그의 위치를 vertical-align과 margin으로 조정합니다.
달력 뷰 버튼 그룹(.calendar-views)은 기존 달력 컨트롤 버튼 그룹(.calendar-controls)처럼 오른쪽에 정렬되도록 설정합니다.
/* calendar-controls */
.calendar > .calendar-header { position: relative; height: 50px; margin: 0px 20px; }
.calendar > .calendar-header > .calendar-yearmonth { display: inline-block; font-size: larger; font-weight: bolder; color: red; line-height: 50px; }
.calendar > .calendar-header > .calendar-controls { display: inline-block; vertical-align: top; margin: 11px 0 0 5px; }
.calendar > .calendar-header > .calendar-controls > button { float: left; width: 25px; height: 28px; padding: 1px 4px 0 4px; border: 1px solid #cbcbcb; background-color: #fff; font-size: 12px; cursor: pointer; }
.calendar > .calendar-header > .calendar-controls > button.calendar-today { width: 40px; }
/* calendar-views */
.calendar > .calendar-header > .calendar-views { position: absolute; top: 11px; right: 0px; }
.calendar > .calendar-header > .calendar-views > button { float: left; width: 40px; height: 28px; padding: 1px 4px 0 4px; border: 1px solid #cbcbcb; background-color: #f8f8f8; font-size: 12px; cursor: pointer; }
.calendar > .calendar-header > .calendar-views > button.active { border-color: #7f4ac8!important; background: #9155e5!important; color: #fff; }
calendarMonth() 함수에서 연간 달력 함수와 공동으로 사용될 임시 공휴일 해시맵을 분리합니다.
// 임시 공휴일 해시맵(HashMap)
var hashmapTemporaryHoliday = [];
hashmapTemporaryHoliday["2022-3-9"] = {"title" : "20대 대통령 선거일"};
임시 공휴일은 Database에서 쿼리로 연도 또는 월 단위로 가져와서 사용하시면 됩니다.
연간 달력 함수 생성
달력 생성 함수(calendar.js)를 사용하여 연간 달력 함수를 생성합니다.
연간 달력은 월이 4열 X 3행으로 나오도록 for() 문으로 처리합니다.
function calendarYear(date) {
// 년
$(".calendar-yearmonth").html(date.getFullYear() + "년");
var options = {
showDay : true,
showToday : true,
arHoliday : hashmapTemporaryHoliday
};
var html = "";
html += "<table>";
html += "<tbody>";
for (var index1 = 0; index1 < 4; index1++) {
html += "<tr>";
for (var index2 = 0; index2 < 3; index2++) {
html += "<td>";
html += "<div class=\"calendar-month\">" + (index1 * 3 + index2 + 1) + "월</div>";
html += calendarHTML(new Date(date.getFullYear(), (index1 * 3 + index2), 1), options);
html += "</td>";
}
html += "</tr>";
}
html += "</tbody>";
html += "</table>";
$("#calendar").attr("data-date", date.getFullYear());
$("#calendar").html(html);
}
달력 뷰 버튼 클릭 이벤트 처리
달력 뷰 버튼에 대한 click 이벤트를 <script>에 추가합니다.
클릭 이벤트가 발생한 버튼에 클래스를 찾아서 연간 버튼인지 월간 버튼인지 확인하여 함수를 호출하게 합니다.
// 달력 뷰 버튼
$(".calendar-views > button").on("click", function(event) {
if ($(event.target).hasClass("calendar-view-year")) {
calendarYear(new Date());
} else if ($(event.target).hasClass("calendar-view-month")) {
calendarMonth(new Date());
}
});
연간 버튼을 클릭하면 현재 연도의 1월부터 12월까지 나옵니다.
달력의 style이 동일하게 적용되다 보니 연간 달력이 크게 나옵니다.
그래서 월간의 style을 수정하고 연간 style를 추가합니다.
<td> 태그와 <span> 태그의 padding를 기본과 월간, 연간으로 분리합니다. 그리고 연간 달력의 폰트 사이즈를 작게하고 월을 왼쪽으로 정렬하여 설정합니다.
.calendar { width: 500px; }
.calendar table { width: 100%; }
.calendar table > caption { font-size: larger; font-weight: bolder; color: red; text-align: left; padding: 10px 20px; }
.calendar table > thead > tr > th.saturday { color: gray; }
.calendar table > thead > tr > th.sunday { color: red; }
.calendar table > tbody > tr > td { text-align: center; vertical-align: top; }
.calendar table > tbody > tr > td > span { display: block; }
.calendar table > tbody > tr > td > span.today { border: 1px solid red; border-radius: 50%; color: white; background-color: red; }
.calendar table > tbody > tr > td > span.saturday { color: gray; }
.calendar table > tbody > tr > td > span.sunday { color: red; }
.calendar table > tbody > tr > td > span.holiday { color: red; }
/* calendar month */
.calendar.month table > tbody > tr > td { padding: 10px 10px; }
.calendar.month table > tbody > tr > td > span { padding: 10px 10px; }
.calendar.month table > tbody > tr > td > span.today { padding: 9px 9px; }
/* calendar year */
.calendar.year table { font-size: 12px; }
.calendar.year table > thead > tr > th { font-weight: normal; }
.calendar.year table > tbody > tr > td > span { padding: 2px 2px; }
.calendar.year .calendar-month { height: 30px; font-size: 14px; font-weight: bolder; text-align: left; line-height: 30px; margin-left: 4px; }
style를 공용으로 사용하기 위해 위의 달력 컨트롤 버튼 그룹과 달력 뷰 버튼 그룹의 style을 포하해서 calendar.css 파일로 분리합니다.
월간과 연간 달력에 style이 적용되도록 calendarMonth() 함수와 calendarYear() 함수를 수정합니다.
클래스가 .calendar인 <div> 태그의 class에 월간이면 "month"를 연간이면 "year"를 추가합니다.
그리고 calendarMonth() 함수에서 연월 표시를 변경합니다.
function calendarMonth(date) {
$(".calendar").removeClass("year");
$(".calendar").addClass("month");
// 년월
$(".calendar-yearmonth").html(date.getFullYear() + "년 " + (date.getMonth() + 1) + "월");
var options = {
showDay : true,
showFullDayName : true,
showToday : true,
arHoliday : hashmapTemporaryHoliday
};
var html = calendarHTML(date, options);
$("#calendar").attr("data-date", date.getFullYear() + "-" + (date.getMonth() + 1));
$("#calendar").html(html);
}
function calendarYear(date) {
$(".calendar").removeClass("month");
$(".calendar").addClass("year");
// 년
$(".calendar-yearmonth").html(date.getFullYear() + "년");
var options = {
showDay : true,
showToday : true,
arHoliday : hashmapTemporaryHoliday
};
var html = "";
html += "<table>";
html += "<tbody>";
for (var index1 = 0; index1 < 4; index1++) {
html += "<tr>";
for (var index2 = 0; index2 < 3; index2++) {
html += "<td>";
html += "<div class=\"calendar-month\">" + (index1 * 3 + index2 + 1) + "월</div>";
html += calendarHTML(new Date(date.getFullYear(), (index1 * 3 + index2), 1), options);
html += "</td>";
}
html += "</tr>";
}
html += "</tbody>";
html += "</table>";
$("#calendar").attr("data-date", date.getFullYear());
$("#calendar").html(html);
}
달력 뷰 버튼 클릭 시 선택된 버튼의 class에 .active를 추가합니다. 선택된 다른 버튼에서는 class에 .active를 삭제합니다.
// 달력 뷰 버튼
$(".calendar-views > button").on("click", function(event) {
$(".calendar-views > button").each(function() {
$(this).removeClass("active");
});
if ($(event.target).hasClass("calendar-view-year")) {
calendarYear(new Date());
} else if ($(event.target).hasClass("calendar-view-month")) {
calendarMonth(new Date());
}
$(event.target).addClass("active");
});
월간 버튼을 클릭하면 현재 월이 나옵니다.
연간 버튼을 클릭하면 현재 연도의 1월부터 12월까지 나옵니다.
이전 버튼과 다음 버튼, 오늘 버튼을 공통으로 사용하기 위해서 버튼 클릭 이벤트 처리 함수에서 클래스가 .calendar인 <div> 태그의 class에서 "month"가 있으면 calendarMonth() 함수로 "year"가 있으면 calendarYear() 함수로 호출되게 처리합니다.
// 이전 이동 버튼
$(".calendar-controls > .calendar-prev").on("click", function() {
if ($(".calendar").hasClass("year")) {
var year = $("#calendar").attr("data-date");
calendarYear(new Date(parseInt(year) - 1, 1, 1));
} else if ($(".calendar").hasClass("month")) {
var yearmonth = $("#calendar").attr("data-date").split("-");
calendarMonth(new Date(parseInt(yearmonth[0]), parseInt(yearmonth[1]) - 2, 1));
}
});
// 다음 이동 버튼
$(".calendar-controls > .calendar-next").on("click", function() {
if ($(".calendar").hasClass("year")) {
var year = $("#calendar").attr("data-date");
calendarYear(new Date(parseInt(year) + 1, 1, 1));
} else if ($(".calendar").hasClass("month")) {
var yearmonth = $("#calendar").attr("data-date").split("-");
calendarMonth(new Date(parseInt(yearmonth[0]), parseInt(yearmonth[1]), 1));
}
});
// 오늘 이동 버튼
$(".calendar-controls > .calendar-today").on("click", function() {
if ($(".calendar").hasClass("year")) {
calendarYear(new Date());
} else if ($(".calendar").hasClass("month")) {
calendarMonth(new Date());
}
});
전체 소스입니다.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>캘린더</title>
<link rel="stylesheet" type="text/css" href="calendar.css" />
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="calendar.js"></script>
</head>
<body>
<div class="calendar">
<div class="calendar-header">
<span class="calendar-yearmonth"></span>
<div class="calendar-controls">
<button type="button" class="calendar-prev"><</button>
<button type="button" class="calendar-next">></button>
<button type="button" class="calendar-today">오늘</button>
</div>
<div class="calendar-views">
<button type="button" class="calendar-view-year">연간</button>
<button type="button" class="calendar-view-month">월간</button>
</div>
</div>
<div id="calendar"></div>
</div>
<script>
// 달력 뷰 버튼
$(".calendar-views > button").on("click", function(event) {
$(".calendar-views > button").each(function() {
$(this).removeClass("active");
});
if ($(event.target).hasClass("calendar-view-year")) {
calendarYear(new Date());
} else if ($(event.target).hasClass("calendar-view-month")) {
calendarMonth(new Date());
}
$(event.target).addClass("active");
});
// 이전 이동 버튼
$(".calendar-controls > .calendar-prev").on("click", function() {
if ($(".calendar").hasClass("year")) {
var year = $("#calendar").attr("data-date");
calendarYear(new Date(parseInt(year) - 1, 1, 1));
} else if ($(".calendar").hasClass("month")) {
var yearmonth = $("#calendar").attr("data-date").split("-");
calendarMonth(new Date(parseInt(yearmonth[0]), parseInt(yearmonth[1]) - 2, 1));
}
});
// 다음 이동 버튼
$(".calendar-controls > .calendar-next").on("click", function() {
if ($(".calendar").hasClass("year")) {
var year = $("#calendar").attr("data-date");
calendarYear(new Date(parseInt(year) + 1, 1, 1));
} else if ($(".calendar").hasClass("month")) {
var yearmonth = $("#calendar").attr("data-date").split("-");
calendarMonth(new Date(parseInt(yearmonth[0]), parseInt(yearmonth[1]), 1));
}
});
// 오늘 이동 버튼
$(".calendar-controls > .calendar-today").on("click", function() {
if ($(".calendar").hasClass("year")) {
calendarYear(new Date());
} else if ($(".calendar").hasClass("month")) {
calendarMonth(new Date());
}
});
// 날짜 클릭
$(document).on("click", ".calendar table > tbody > tr > td", function(event) {
//event.preventDefault();
event.stopPropagation();
var eventTarget = event.target;
while (eventTarget.tagName != "TD") {
eventTarget = eventTarget.parentElement;
}
if ($(eventTarget).attr("data-date") != undefined) {
alert($(eventTarget).attr("data-date"));
}
});
// 임시 공휴일 해시맵(HashMap)
var hashmapTemporaryHoliday= [];
hashmapTemporaryHoliday["2022-3-9"] = {"title" : "20대 대통령 선거일"};
function calendarMonth(date) {
$(".calendar").removeClass("year");
$(".calendar").addClass("month");
// 년월
$(".calendar-yearmonth").html(date.getFullYear() + "년 " + (date.getMonth() + 1) + "월");
var options = {
showDay : true,
showFullDayName : true,
showToday : true,
arHoliday : hashmapTemporaryHoliday
};
var html = calendarHTML(date, options);
$("#calendar").attr("data-date", date.getFullYear() + "-" + (date.getMonth() + 1));
$("#calendar").html(html);
}
function calendarYear(date) {
$(".calendar").removeClass("month");
$(".calendar").addClass("year");
// 년
$(".calendar-yearmonth").html(date.getFullYear() + "년");
var options = {
showDay : true,
showToday : true,
arHoliday : hashmapTemporaryHoliday
};
var html = "";
html += "<table>";
html += "<tbody>";
for (var index1 = 0; index1 < 4; index1++) {
html += "<tr>";
for (var index2 = 0; index2 < 3; index2++) {
html += "<td>";
html += "<div class=\"calendar-month\">" + (index1 * 3 + index2 + 1) + "월</div>";
html += calendarHTML(new Date(date.getFullYear(), (index1 * 3 + index2), 1), options);
html += "</td>";
}
html += "</tr>";
}
html += "</tbody>";
html += "</table>";
$("#calendar").attr("data-date", date.getFullYear());
$("#calendar").html(html);
}
calendarMonth(new Date());
$(".calendar-view-month").addClass("active");
</script>
</body>
</html>
월간과 연간에서 이전 이동 버튼과 다음 이동 버튼을 클릭하여 테스트해 보시기 바랍니다.