티스토리 뷰
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>6주 고정형 일정 관리 달력</title>
<style>
:root {
--primary: #4a90e2;
--today-bg: #fff9c4;
--border-color: #eee;
}
body { font-family: 'Malgun Gothic', sans-serif; display: flex; justify-content: center; padding: 20px; background: #f8f9fa; }
.calendar-container {
width: 450px;
background: #fff;
padding: 20px;
border-radius: 15px;
box-shadow: 0 8px 30px rgba(0,0,0,0.08);
}
/* 헤더 설정 */
.calendar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.nav-group { display: flex; align-items: center; gap: 5px; }
select { padding: 4px 8px; border: 1px solid #ddd; border-radius: 4px; }
button { cursor: pointer; border: none; border-radius: 4px; padding: 5px 10px; background: var(--primary); color: white; }
button:hover { opacity: 0.8; }
/* 요일 및 그리드 */
.weekdays { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; font-weight: bold; padding-bottom: 10px; color: #555; }
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
border-top: 1px solid var(--border-color);
}
.day {
aspect-ratio: 1 / 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 14px;
border-bottom: 1px solid var(--border-color);
position: relative;
}
.day:hover { background: #f0f7ff; }
.day.today { background: var(--today-bg); font-weight: bold; border-radius: 4px; }
.day.other-month { color: #ccc; cursor: default; } /* 이전/다음달 날짜 색상 */
/* 일정 점 표시 */
.has-event::after {
content: '';
position: absolute;
bottom: 6px;
width: 5px;
height: 5px;
background: #ff4d4d;
border-radius: 50%;
}
/* 하단 일정 리스트 */
.event-section { margin-top: 20px; border-top: 2px solid #333; padding-top: 15px; min-height: 150px; }
.event-section h3 { font-size: 16px; margin: 0 0 10px 0; color: #333; }
.event-item { display: flex; align-items: center; margin-bottom: 8px; list-style: none; }
.badge {
padding: 2px 6px; border-radius: 3px; color: white; font-size: 11px; margin-right: 8px; font-weight: bold;
}
.bg-전제 { background: #ff9800; }
.bg-지역 { background: #4caf50; }
.no-event { color: #999; font-size: 14px; list-style: none; }
</style>
</head>
<body>
<div class="calendar-container">
<div class="calendar-header">
<div class="nav-group">
<button id="prevMonth"><</button>
<select id="yearSelect"></select>
<select id="monthSelect"></select>
<button id="nextMonth">></button>
</div>
<button onclick="goToToday()" style="background: #666;">오늘</button>
</div>
<div class="weekdays">
<div style="color:red">일</div><div>월</div><div>화</div><div>수</div><div>목</div><div>금</div><div style="color:blue">토</div>
</div>
<div id="calendarGrid" class="calendar-grid"></div>
<div class="event-section">
<h3 id="selectedDateTitle">날짜 선택</h3>
<ul id="eventList" style="padding:0"></ul>
</div>
</div>
<script>
// 1. 데이터 설정
const scheduleData = {
"2026-03-03": [{category: "전제", content: "프로젝트 킥오프"}, {category: "지역", content: "나주 출장"}],
"2026-03-15": [{category: "지역", content: "강원지사 미팅"}],
"2026-04-01": [{category: "전제", content: "만우절 이벤트"}]
};
let viewDate = new Date();
function init() {
const yearSelect = document.getElementById('yearSelect');
const monthSelect = document.getElementById('monthSelect');
const curYear = new Date().getFullYear();
for (let i = curYear - 5; i <= curYear + 5; i++) {
yearSelect.add(new Option(i + "년", i));
}
for (let i = 0; i < 12; i++) {
monthSelect.add(new Option((i + 1) + "월", i));
}
renderCalendar();
showEvents(formatDate(new Date()));
}
function formatDate(date) {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
}
// 2. 6주(42일) 고정 렌더링 로직
function renderCalendar() {
const grid = document.getElementById('calendarGrid');
const year = viewDate.getFullYear();
const month = viewDate.getMonth();
document.getElementById('yearSelect').value = year;
document.getElementById('monthSelect').value = month;
grid.innerHTML = '';
// 달력의 시작점 계산 (이전 달의 남은 날짜 포함)
const startOfMonth = new Date(year, month, 1);
const startDay = startOfMonth.getDay(); // 0(일) ~ 6(토)
// 달력 첫 칸에 들어갈 날짜 계산 (첫날 - 요일수)
const calendarStartDate = new Date(year, month, 1 - startDay);
const todayStr = formatDate(new Date());
// 무조건 42회 반복 (7일 * 6주)
for (let i = 0; i < 42; i++) {
const currentLoopDate = new Date(calendarStartDate);
currentLoopDate.setDate(calendarStartDate.getDate() + i);
const dateStr = formatDate(currentLoopDate);
const dayDiv = document.createElement('div');
dayDiv.className = 'day';
// 현재 월이 아닌 날짜 처리
if (currentLoopDate.getMonth() !== month) {
dayDiv.classList.add('other-month');
} else {
// 현재 월인 경우에만 클릭 및 일정 표시 활성화
if (dateStr === todayStr) dayDiv.classList.add('today');
if (scheduleData[dateStr]) dayDiv.classList.add('has-event');
dayDiv.onclick = () => showEvents(dateStr);
}
dayDiv.innerText = currentLoopDate.getDate();
grid.appendChild(dayDiv);
}
}
function showEvents(dateStr) {
document.getElementById('selectedDateTitle').innerText = dateStr;
const listEl = document.getElementById('eventList');
const events = scheduleData[dateStr] || [];
if (events.length > 0) {
listEl.innerHTML = events.map(ev => `
<li class="event-item">
<span class="badge bg-${ev.category}">${ev.category}</span>
<span>${ev.content}</span>
</li>
`).join('');
} else {
listEl.innerHTML = `<li class="no-event">표시할 일정이 없습니다.</li>`;
}
}
function goToToday() { viewDate = new Date(); renderCalendar(); showEvents(formatDate(viewDate)); }
document.getElementById('yearSelect').onchange = (e) => { viewDate.setFullYear(e.target.value); renderCalendar(); };
document.getElementById('monthSelect').onchange = (e) => { viewDate.setMonth(e.target.value); renderCalendar(); };
document.getElementById('prevMonth').onclick = () => { viewDate.setMonth(viewDate.getMonth() - 1); renderCalendar(); };
document.getElementById('nextMonth').onclick = () => { viewDate.setMonth(viewDate.getMonth() + 1); renderCalendar(); };
init();
</script>
</body>
</html>
'WEB > JavaScript' 카테고리의 다른 글
| 카테고리 기능이 추가된 달력 스크립트 (년/월 이동 + 오늘 일정 자동 표시) (0) | 2026.03.03 |
|---|---|
| 통합 달력 스크립트 (년/월 이동 + 오늘 일정 자동 표시) (0) | 2026.03.03 |
| 실시간 검색 필터링 (0) | 2026.01.14 |
| [JavaScript]슬라이드 이미지 (0) | 2026.01.03 |
| [JavaScript] 비밀번호가 8자 이상이고 특수문자가 포함여부 체크하기 (0) | 2026.01.03 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- sw기술자평균임금 #2025년 sw기술자 평균임금
- 와이파이증폭기추천 #와이파이설치
- 증폭기 #아이피타임증폭기
- 테스크탑무선랜카드 #무선랜카드 #아이피타이무선랜카드 #a3000mini #무선랜카드추천
- 무료폰트 #무료웹폰트 #한수원한돋움 #한수원한울림 #한울림체 #한돋움체
- SQL명령어 #SQL
- 탭메뉴자바스크립트
- thymeleaf
- 자바스크립트break
- jdk #jre
- css미디어쿼리 #미디어쿼리 #mediaquery
- 바지락칼국수 #월곡동칼국수 #칼국수맛집
- 자바스크립트countiue
- 자바스크립트정규표현식
- 자바스크립트 #javascript #math
- 파비콘사이즈
- iptime와이파이증폭기 #와이파이증폭기설치
- 쇼팬하우어 #좋은책
- jQuery #jQuery이미지슬라이드 #이미지슬라이드
- ajax
- 광주분식 #광주분식맛집 #상추튀김 #상추튀김맛집 #광주상추튀김
- 썬크림 #닥터지썬크림 #내돈내산 #내돈내산썬크림 #썬크림추천 #spf50썬크림 #닥터지메디유브이울트라선
- lg그램pro #lg그램 #노트북 #노트북추천 #lg노트북
- // 사진직: 데이터가 없으면 DEFAULT_IMG 사용 const profileSrc = (d.img && d.img !== "") ? d.img : DEFAULT_IMG;('#user-photo').attr('src'
- echart
- 파비콘 #파비콘 사이트에 적용
- 좋은책 #밥프록터 #부의원리
- 연명의료결정제도 #사전연명의료의향서 #사전연명의료의향서등록기관 #광주사전연명의료의향서
- 좋은책
- 정보처리기사 #정보처리기사요약 #정보처리기사요점정리
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
글 보관함

