下面我将为你提供一个完整、可运行的示例,并详细解释每一部分的作用,这个示例将包含:

网页 日历 日程 javascript
(图片来源网络,侵删)
  1. 一个月视图的日历网格。
  2. 点击日期可以添加、查看和删除日程。
  3. 数据会保存在浏览器的 localStorage 中,刷新页面后数据不会丢失。

最终效果预览


HTML 结构 (index.html)

这是页面的骨架,定义了日历的标题、星期几的表头、日期网格,以及用于添加日程的模态框。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">JavaScript 日程日历</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="calendar-container">
        <header class="calendar-header">
            <button id="prevMonth">&lt;</button>
            <h1 id="currentMonth"></h1>
            <button id="nextMonth">&gt;</button>
        </header>
        <div class="calendar-grid">
            <!-- 星期几的表头 -->
            <div class="weekdays">
                <div>日</div>
                <div>一</div>
                <div>二</div>
                <div>三</div>
                <div>四</div>
                <div>五</div>
                <div>六</div>
            </div>
            <!-- 日期格子将通过 JavaScript 动态生成 -->
            <div id="daysContainer" class="days"></div>
        </div>
    </div>
    <!-- 添加/编辑日程的模态框 -->
    <div id="eventModal" class="modal">
        <div class="modal-content">
            <span class="close">&times;</span>
            <h2 id="modalTitle">添加日程</h2>
            <form id="eventForm">
                <input type="hidden" id="eventDate">
                <input type="text" id="eventTitle" placeholder="日程标题" required>
                <textarea id="eventDescription" placeholder="日程描述 (可选)"></textarea>
                <button type="submit">保存</button>
            </form>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

CSS 样式 (style.css)

这部分负责美化日历,让它看起来更美观、易用,它定义了布局、颜色、字体和交互效果(如悬停和模态框)。

body {
    font-family: 'Arial', sans-serif;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    margin: 0;
}
.calendar-container {
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
    width: 90%;
    max-width: 800px;
    overflow: hidden;
}
.calendar-header {
    background-color: #007bff;
    color: white;
    padding: 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.calendar-header h1 {
    margin: 0;
    font-size: 1.5em;
}
.calendar-header button {
    background: none;
    border: none;
    color: white;
    font-size: 1.5em;
    cursor: pointer;
    padding: 0 10px;
    transition: background-color 0.2s;
}
.calendar-header button:hover {
    background-color: rgba(255, 255, 255, 0.2);
}
.weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    background-color: #e9ecef;
    text-align: center;
    font-weight: bold;
    padding: 10px 0;
}
.days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    background-color: #ddd;
    padding: 1px;
}
.day {
    background-color: #fff;
    min-height: 100px;
    padding: 10px;
    box-sizing: border-box;
    cursor: pointer;
    transition: background-color 0.2s;
    position: relative;
}
.day:hover {
    background-color: #f8f9fa;
}
.day.other-month {
    color: #ccc;
    background-color: #fafafa;
}
.day.today {
    background-color: #e3f2fd;
    font-weight: bold;
}
.day-number {
    font-size: 0.9em;
    margin-bottom: 5px;
}
.event {
    background-color: #007bff;
    color: white;
    padding: 2px 5px;
    margin: 2px 0;
    border-radius: 3px;
    font-size: 0.75em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
}
.event:hover {
    background-color: #0056b3;
}
/* 模态框样式 */
.modal {
    display: none; /* 默认隐藏 */
    position: fixed;
    z-index: 1000;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
    background-color: #fefefe;
    margin: 15% auto;
    padding: 20px;
    border: 1px solid #888;
    width: 80%;
    max-width: 500px;
    border-radius: 5px;
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
}
.close:hover,
.close:focus {
    color: black;
    text-decoration: none;
}
#eventForm {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
#eventForm input, #eventForm textarea {
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 1em;
}
#eventForm button {
    padding: 10px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 1em;
}
#eventForm button:hover {
    background-color: #0056b3;
}

JavaScript 逻辑 (script.js)

这是整个应用的核心,负责处理所有的交互逻辑,包括渲染日历、切换月份、管理日程数据等。

document.addEventListener('DOMContentLoaded', () => {
    // --- DOM 元素 ---
    const currentMonthElement = document.getElementById('currentMonth');
    const daysContainer = document.getElementById('daysContainer');
    const prevMonthButton = document.getElementById('prevMonth');
    const nextMonthButton = document.getElementById('nextMonth');
    const eventModal = document.getElementById('eventModal');
    const eventForm = document.getElementById('eventForm');
    const modalTitle = document.getElementById('modalTitle');
    const closeModal = document.querySelector('.close');
    // --- 状态管理 ---
    let currentDate = new Date();
    let events = JSON.parse(localStorage.getItem('calendarEvents')) || {};
    let selectedDate = null;
    let editingEventId = null;
    // --- 辅助函数 ---
    const monthNames = [
        "一月", "二月", "三月", "四月", "五月", "六月",
        "七月", "八月", "九月", "十月", "十一月", "十二月"
    ];
    function saveEventsToLocalStorage() {
        localStorage.setItem('calendarEvents', JSON.stringify(events));
    }
    function formatDateKey(date) {
        return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
    }
    function generateUniqueId() {
        return Date.now().toString(36) + Math.random().toString(36).substr(2);
    }
    // --- 渲染函数 ---
    function renderCalendar() {
        const year = currentDate.getFullYear();
        const month = currentDate.getMonth();
        // 更新月份标题
        currentMonthElement.textContent = `${year}年 ${monthNames[month]}`;
        // 清空日期容器
        daysContainer.innerHTML = '';
        // 获取当月第一天和最后一天
        const firstDayOfMonth = new Date(year, month, 1);
        const lastDayOfMonth = new Date(year, month + 1, 0);
        // 获取第一天是星期几 (0 = 星期日, 6 = 星期六)
        const firstDayOfWeek = firstDayOfMonth.getDay();
        // 获取上个月的最后几天
        const prevMonthLastDay = new Date(year, month, 0).getDate();
        for (let i = firstDayOfWeek - 1; i >= 0; i--) {
            const dayNumber = prevMonthLastDay - i;
            const dayElement = createDayElement(new Date(year, month - 1, dayNumber), true);
            daysContainer.appendChild(dayElement);
        }
        // 渲染当月的所有天
        for (let day = 1; day <= lastDayOfMonth.getDate(); day++) {
            const dayElement = createDayElement(new Date(year, month, day), false);
            daysContainer.appendChild(dayElement);
        }
        // 渲染下个月的前几天,以填满6x7的网格
        const totalCells = daysContainer.children.length;
        const remainingCells = 42 - totalCells; // 6行 x 7列
        for (let day = 1; day <= remainingCells; day++) {
            const dayElement = createDayElement(new Date(year, month + 1, day), true);
            daysContainer.appendChild(dayElement);
        }
    }
    function createDayElement(date, isOtherMonth) {
        const dayElement = document.createElement('div');
        dayElement.className = 'day';
        if (isOtherMonth) {
            dayElement.classList.add('other-month');
        }
        // 标记今天
        const today = new Date();
        if (date.toDateString() === today.toDateString()) {
            dayElement.classList.add('today');
        }
        const dayNumberElement = document.createElement('div');
        dayNumberElement.className = 'day-number';
        dayNumberElement.textContent = date.getDate();
        dayElement.appendChild(dayNumberElement);
        // 为选定的日期添加日程
        const dateKey = formatDateKey(date);
        if (events[dateKey]) {
            events[dateKey].forEach(event => {
                const eventElement = document.createElement('div');
                eventElement.className = 'event';
                eventElement.textContent = event.title;
                eventElement.dataset.eventId = event.id;
                eventElement.addEventListener('click', (e) => {
                    e.stopPropagation(); // 阻止事件冒泡到 dayElement
                    editEvent(event.id);
                });
                dayElement.appendChild(eventElement);
            });
        }
        // 点击日期添加新日程
        dayElement.addEventListener('click', () => {
            selectedDate = date;
            openModal();
        });
        return dayElement;
    }
    // --- 模态框函数 ---
    function openModal(date = null, eventId = null) {
        editingEventId = eventId;
        if (date) {
            selectedDate = date;
        }
        if (editingEventId) {
            modalTitle.textContent = '编辑日程';
            const dateKey = formatDateKey(selectedDate);
            const event = events[dateKey].find(e => e.id === editingEventId);
            document.getElementById('eventTitle').value = event.title;
            document.getElementById('eventDescription').value = event.description || '';
        } else {
            modalTitle.textContent = '添加日程';
            eventForm.reset();
        }
        document.getElementById('eventDate').value = formatDateKey(selectedDate);
        eventModal.style.display = 'block';
    }
    function closeModalFunc() {
        eventModal.style.display = 'none';
        eventForm.reset();
        editingEventId = null;
    }
    function editEvent(eventId) {
        // 需要知道这个日程属于哪一天
        for (const dateKey in events) {
            const event = events[dateKey].find(e => e.id === eventId);
            if (event) {
                openModal(new Date(dateKey), eventId);
                return;
            }
        }
    }
    // --- 事件监听器 ---
    prevMonthButton.addEventListener('click', () => {
        currentDate.setMonth(currentDate.getMonth() - 1);
        renderCalendar();
    });
    nextMonthButton.addEventListener('click', () => {
        currentDate.setMonth(currentDate.getMonth() + 1);
        renderCalendar();
    });
    closeModal.addEventListener('click', closeModalFunc);
    window.addEventListener('click', (event) => {
        if (event.target === eventModal) {
            closeModalFunc();
        }
    });
    eventForm.addEventListener('submit', (e) => {
        e.preventDefault();
        const dateKey = document.getElementById('eventDate').value;
        const title = document.getElementById('eventTitle').value;
        const description = document.getElementById('eventDescription').value;
        if (!events[dateKey]) {
            events[dateKey] = [];
        }
        if (editingEventId) {
            // 编辑现有日程
            const eventIndex = events[dateKey].findIndex(e => e.id === editingEventId);
            if (eventIndex !== -1) {
                events[dateKey][eventIndex] = { id: editingEventId, title, description };
            }
        } else {
            // 添加新日程
            const newEvent = {
                id: generateUniqueId(),
                title,
                description
            };
            events[dateKey].push(newEvent);
        }
        saveEventsToLocalStorage();
        renderCalendar();
        closeModalFunc();
    });
    // --- 初始化 ---
    renderCalendar();
});

如何使用

  1. 创建文件:创建三个文件,分别命名为 index.htmlstyle.cssscript.js
  2. 复制代码:将上面提供的代码分别粘贴到对应的文件中。
  3. 打开网页:用浏览器打开 index.html 文件。

功能扩展建议

这个基础版本已经可以满足基本需求,你可以在此基础上进行扩展:

网页 日历 日程 javascript
(图片来源网络,侵删)
  1. 周视图/日视图:增加切换按钮,实现周视图和日视图的渲染逻辑。
  2. 日程提醒:使用 Notification API 在特定时间弹出桌面提醒。
  3. 拖拽功能:允许用户拖拽日程到其他日期。
  4. 数据同步:连接后端 API,将日程数据存储在服务器上,实现多设备同步。
  5. UI/UX 优化:增加动画效果、更丰富的颜色主题、右键菜单等。
  6. 重复日程:支持添加每天、每周、每月重复的日程。
网页 日历 日程 javascript
(图片来源网络,侵删)