最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何基于Grid布局完成最精簡的日期組件

 更新時間:2026年03月04日 11:28:12   作者:橙某人  
這篇文章主要介紹了如何基于Grid布局完成最精簡的日期組件的相關(guān)資料,Grid布局是CSS3中新增的一種布局方式,它是一種基于網(wǎng)格線的布局方式,可以將網(wǎng)頁劃分為多個區(qū)域,并在這些區(qū)域中放置內(nèi)容,需要的朋友可以參考下

需求背景

咱們在日常開發(fā)中,日期或日歷組件幾乎是每個前端開發(fā)者都會遇到的需求。

大多數(shù)時候我們的項目中肯定有一個組件庫,如element-plus, antd 等等,這些組件庫基本都是有日期這類標(biāo)配組件,或者市面上也有很多現(xiàn)成庫,能夠輕松解決這類需求。

但是,如遇到比較有理想的產(chǎn)品經(jīng)理時??,現(xiàn)成組件或魔改組件庫可能都無法滿足,此時自定義組件就很有必要。日期組件開發(fā)細節(jié)非常多,看似麻煩,但我們可以 "抽繁尋簡",先聚焦解決其核心部分,接下來咱就一起來開啟這段奇妙旅程吧,嘿嘿。

基礎(chǔ)教學(xué)

第一步:搭建基礎(chǔ)HTML結(jié)構(gòu)

咱們從最簡單的布局來看,為了簡單好理解,結(jié)構(gòu)直接復(fù)制就行哈。

<!DOCTYPE html>
<html>
<body>
    <div class="calendar-wrapper">
        <h1>2025年08月</h1>
        <ul class="calendar">
            <li class="weekday">一</li>
            <li class="weekday">二</li>
            <li class="weekday">三</li>
            <li class="weekday">四</li>
            <li class="weekday">五</li>
            <li class="weekday">六</li>
            <li class="weekday">日</li>
            
            <li class="first-day">1</li>
            <li>2</li>
            <li class="today">3</li>
            <li>4</li>
            <li>5</li>
            <!-- 更多日期... -->
            <li>31</li>
        </ul>
    </div>
</body>
</html>

這里小編用了 ulli 標(biāo)簽來構(gòu)建日期結(jié)構(gòu),讓它語義化更好,也便于 CSS Grid 布局。

第二步:CSS Grid 布局

樣式咱們這里我們采用 Grid 布局,它能夠快速來完成所需布局,其他方式當(dāng)然也可以,如 Flex 等。

body {
    padding: 0;
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
}
.calendar-wrapper {
    border-radius: 20px;
    padding: 30px;
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
    width: 400px;
    margin-top: 100px;
}
h1 {
    text-align: center;
    color: #333;
    margin-bottom: 20px;
    margin-top: 0;
}
/* ?? 核心Grid布局 - 就這3行! */
.calendar {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    list-style: none;
    padding: 0;
    margin: 0;
}
/* 8月1日從星期四開始 */
.first-day {
    grid-column-start: 5;
}
.calendar li {
    aspect-ratio: 1; /* 保持正方形 */
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-weight: 500;
    transition: all 0.2s ease;
}
.weekday {
    background-color: #190f01;
    color: #fff;
    margin-bottom: 2px;
}
.calendar li:not(.weekday):hover {
    background-color: #f4b225;
    color: #fff;
    transform: scale(1.1);
    cursor: pointer;
}
.today {
    background-color: #f4b225;
    color: #fff;
}

效果:

非常簡單的就完成日期組件的布局!?? 核心就五行代碼:

  • display: grid: 啟用 Grid 布局
  • grid-template-columns: repeat(7, 1fr): 創(chuàng)建7列等寬布局(一周7天)。
  • gap: 1px: 設(shè)置網(wǎng)格間距。
  • aspect-ratio: 1:讓每個日期格子保持正方形,無論屏幕大小如何變化都能保持完美比例!?
  • grid-column-start: 5:讓月份的第一天從正確的星期位置開始顯示。比如8月1日是星期五,那就從第5列開始!

高級教學(xué)

靜態(tài)日歷的基本布局就此完成啦,接下來,咱們用上 JS 來給它注入點靈魂。

我們要完成的功能有??:

  • 動態(tài)來生成 HTML 結(jié)構(gòu)。
  • 補全當(dāng)前月份的上下月份的天數(shù)。
  • 切換月份。
  • 選擇日期。

首先,我們需要一個更完整的HTML結(jié)構(gòu):

<div class="calendar-wrapper">
    <div class="header">
        <span class="arrow" id="prevMonth">?</span>
        <h1 id="monthYear"></h1>
        <span class="arrow" id="nextMonth">?</span>
    </div>
    <ul class="calendar" id="calendar"></ul>
</div>
<div class="selected-date-info" id="selectedDateInfo">點擊日期進行選擇</div>

樣式調(diào)整:

.header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}
h1 {
    text-align: center;
    color: #333;
    margin: 0;
    flex: 1;
}
.arrow {
    cursor: pointer;
    font-size: 24px;
    color: #333;
    padding: 10px;
    border-radius: 50%;
    transition: all 0.2s ease;
    user-select: none;
}
.other-month {
    color: #ccc;
}
.other-month:hover {
    background-color: #e0e0e0;
    color: #666;
}

然后,JS 的核心代碼,這里小編采用了 ES6 的 class 語法:

/**
 * @name 日歷類,用于生成和管理日期或日歷相關(guān)的功能
 */
class Calendar {
    constructor() {
        this.currentDate = new Date();
        this.today = new Date();
        this.monthNames = [
            '01月', '02月', '03月', '04月', '05月', '06月',
            '07月', '08月', '09月', '10月', '11月', '12月'
        ];
        this.weekdays = ['一', '二', '三', '四', '五', '六', '日'];
        this.init();
    }
    /** @name 初始化 **/
    init() {
        this.bindEvents();
        this.render();
    }
    /** @name 綁定事件 **/
    bindEvents() {
        document.getElementById('prevMonth').addEventListener('click', () => {
            this.previousMonth();
        });
        document.getElementById('nextMonth').addEventListener('click', () => {
            this.nextMonth();
        });
    }
    /**
     * @name 獲取某月的第一天是星期幾
     * @param {number} year - 年份
     * @param {number} month - 月份(0-11)
     * @returns {number} 星期幾(調(diào)整為周一開始:0=周一,6=周日)
     */
    getFirstDayOfMonth(year, month) {
        const firstDay = new Date(year, month, 1).getDay();
        // 調(diào)整為周一開始:周日(0)變成6,其他減1
        return firstDay === 0 ? 6 : firstDay - 1;
    }
    /**
     * @name 獲取某月的天數(shù)
     * @param {number} year - 年份
     * @param {number} month - 月份(0-11)
     * @returns {number} 天數(shù)
     */
    getDaysInMonth(year, month) {
        return new Date(year, month + 1, 0).getDate();
    }
    /**
     * @name 檢查是否是今天
     * @param {number} year - 年份
     * @param {number} month - 月份(0-11)
     * @param {number} day - 日期
     * @returns {boolean} 是否是今天
     */
    isToday(year, month, day) {
        return year === this.today.getFullYear() &&
               month === this.today.getMonth() &&
               day === this.today.getDate();
    }
    /** @name 渲染組件 **/
    render() {
        const year = this.currentDate.getFullYear();
        const month = this.currentDate.getMonth();
        // 更新標(biāo)題
        document.getElementById('monthYear').textContent = 
            `${year}年${this.monthNames[month]}`;
        // 清空
        const calendar = document.getElementById('calendar');
        calendar.innerHTML = '';
        // 添加星期標(biāo)題
        this.weekdays.forEach(day => {
            const li = document.createElement('li');
            li.className = 'weekday';
            li.textContent = day;
            calendar.appendChild(li);
        });
        // 獲取當(dāng)月信息
        const firstDay = this.getFirstDayOfMonth(year, month);
        const daysInMonth = this.getDaysInMonth(year, month);
        const daysInPrevMonth = this.getDaysInMonth(year, month - 1);
        // 添加上個月的尾部日期
        for (let i = firstDay - 1; i >= 0; i--) {
            const li = document.createElement('li');
            li.className = 'other-month';
            li.textContent = daysInPrevMonth - i;
            calendar.appendChild(li);
        }
        // 添加當(dāng)月日期
        for (let day = 1; day <= daysInMonth; day++) {
            const li = document.createElement('li');
            li.textContent = day;
            if (this.isToday(year, month, day)) {
                li.className = 'today';
            }
            calendar.appendChild(li);
        }
        // 添加下個月的開頭日期,補齊6行
        const totalCells = calendar.children.length;
        const remainingCells = 42 - totalCells; // 6行 × 7列 = 42個格子
        for (let day = 1; day <= remainingCells; day++) {
            const li = document.createElement('li');
            li.className = 'other-month';
            li.textContent = day;
            calendar.appendChild(li);
        }
    }
    /** @name 上一個月 **/
    previousMonth() {
        this.currentDate.setMonth(this.currentDate.getMonth() - 1);
        this.render();
    }
    /** @name 下一個月 **/
    nextMonth() {
        this.currentDate.setMonth(this.currentDate.getMonth() + 1);
        this.render();
    }
}

new Calendar();

挺簡單哈,就一個類搞定,設(shè)計思路?:

  • 構(gòu)造函數(shù)初始化基本數(shù)據(jù)。
  • init() 方法負責(zé)初始化啟動。
  • render() 方法負責(zé)渲染。
  • bindEvents() 方法負責(zé)事件綁定。

效果:

基于這個設(shè)計過程,咱們繼續(xù)來實現(xiàn)日期的點擊??選擇功能,這個稍微比較麻煩一些。

先把樣式整上:

.selected {
    background-color: #007bff  !important;
    color: #fff;
    box-shadow: 0 0 0 2px #007bff;
}
.selected.today {
    background-color: #007bff;
    box-shadow: 0 0 0 2px #f4b225;
}
.selected-date-info {
    margin-top: 20px;
    padding: 15px;
    background-color: #f4b225;
    color: #fff;
    border-radius: 10px;
    text-align: center;
    font-size: 16px;
    min-height: 20px;
    font-weight: bold;
}

Calendar 類中添加選擇功能相關(guān)的屬性和方法:

class Calendar {
    constructor() {
        this.currentDate = new Date();
        this.today = new Date();
        this.selectedDate = null; // 新增:選中的日期
        
        // ...
    }
    bindEvents() {
        // ...
        // 添加點擊的事件委托
        document.getElementById('calendar').addEventListener('click', (e) => {
            this.handleDateClick(e);
        });
    }
    /**
     * @name 處理日期點擊事件
     * @param {Event} e - 點擊事件
     */
    handleDateClick(e) {
        const target = e.target;
        // 只處理日期元素的點擊,排除星期標(biāo)題
        if (target.tagName === 'LI' && !target.classList.contains('weekday')) {
            const day = parseInt(target.textContent);
            const year = this.currentDate.getFullYear();
            let month = this.currentDate.getMonth();
            // 處理其他月份的日期
            if (target.classList.contains('other-month')) {
                // 判斷是上個月還是下個月
                const firstDay = this.getFirstDayOfMonth(year, month);
                const daysInMonth = this.getDaysInMonth(year, month);
                const totalCurrentMonthCells = firstDay + daysInMonth;
                const clickedIndex = Array.from(target.parentNode.children).indexOf(target);
                if (clickedIndex < firstDay + 7) { // 上個月
                    month = month - 1;
                    if (month < 0) {
                        month = 11;
                        year = year - 1;
                    }
                } else { // 下個月
                    month = month + 1;
                    if (month > 11) {
                        month = 0;
                        year = year + 1;
                    }
                }
            }
            // 設(shè)置選中日期
            this.selectedDate = new Date(year, month, day);
            // 更新顯示
            this.updateSelectedDateDisplay();
            this.updateCalendarSelection();
        }
    }
}

鼠標(biāo)點擊后,我們需要更新頁面的選中狀態(tài),小編用兩個方法來維護頁面UI的變化:

/** @name 更新選中日期的顯示 **/
updateSelectedDateDisplay() {
    const infoElement = document.getElementById('selectedDateInfo');
    if (this.selectedDate) {
        const year = this.selectedDate.getFullYear();
        const month = this.selectedDate.getMonth() + 1;
        const day = this.selectedDate.getDate();
        const weekDay = ['日', '一', '二', '三', '四', '五', '六'][this.selectedDate.getDay()];
        infoElement.textContent = `選中日期: ${year}年${month.toString().padStart(2, '0')}月${day.toString().padStart(2, '0')}日 星期${weekDay}`;
    } else {
        infoElement.textContent = '點擊日期進行選擇';
    }
}
 /** @name 更新選中狀態(tài) **/
updateCalendarSelection() {
    const calendarItems = document.querySelectorAll('#calendar li:not(.weekday)');
    // 清除所有選中狀態(tài)
    calendarItems.forEach(item => {
        item.classList.remove('selected');
    });
    // 如果有選中日期,標(biāo)記對應(yīng)的日期元素
    if (this.selectedDate) {
        const selectedYear = this.selectedDate.getFullYear();
        const selectedMonth = this.selectedDate.getMonth();
        const selectedDay = this.selectedDate.getDate();
        const currentYear = this.currentDate.getFullYear();
        const currentMonth = this.currentDate.getMonth();
        // 只在當(dāng)前顯示的月份中標(biāo)記選中狀態(tài)
        if (selectedYear === currentYear && selectedMonth === currentMonth) {
            calendarItems.forEach(item => {
                if (parseInt(item.textContent) === selectedDay && !item.classList.contains('other-month')) {
                    item.classList.add('selected');
                }
            });
        }
    }
}

為了方便外部調(diào)用,提供一些實用的 API 方法:

/**
 * @name 獲取選中的日期
 * @returns {Date|null} 選中的日期對象
 */
getSelectedDate() {
    return this.selectedDate;
}
/**
 * @name 設(shè)置選中的日期
 * @param {Date} date - 要選中的日期
 */
setSelectedDate(date) {
    this.selectedDate = date;
    this.updateSelectedDateDisplay();
    this.updateCalendarSelection();
}
/** @name 清除選中狀態(tài) **/
clearSelection() {
    this.selectedDate = null;
    this.updateSelectedDateDisplay();
    this.updateCalendarSelection();
}

最后,由于天數(shù)是根據(jù)月份動態(tài)渲染的,別忘了在月份切換時也要更新選中狀態(tài)的顯示:

previousMonth() {
    this.currentDate.setMonth(this.currentDate.getMonth() - 1);
    this.render();
    // 重新渲染后更新選中狀態(tài)
    this.updateCalendarSelection();
}

nextMonth() {
    this.currentDate.setMonth(this.currentDate.getMonth() + 1);
    this.render();
    // 重新渲染后更新選中狀態(tài)
    this.updateCalendarSelection();
}

效果:

總結(jié)

通過這篇文章,咱們從零開始實現(xiàn)了一個完整的日期組件,整個過程中,最讓小編印象深刻的就是 CSS Grid 的強大!在這種場景下,布局這塊絕對非它莫屬。??

然后呢,學(xué)會了基礎(chǔ)版本,你還可以繼續(xù)擴展:

  • 添加事件標(biāo)記: 在特定日期顯示小圓點。
  • 日期選擇功能: 支持單選或多選日期。
  • 農(nóng)歷顯示: 在公歷下方顯示農(nóng)歷。
  • 主題切換: 支持多種顏色主題。
  • 動畫效果: 月份切換時的滑動動畫。
  • ...

那就沒啦~??

到此這篇關(guān)于如何基于Grid布局完成最精簡的日期組件的文章就介紹到這了,更多相關(guān)Grid布局日期組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

白水县| 凤城市| 将乐县| 乾安县| 民丰县| 项城市| 郁南县| 长岭县| 两当县| 太谷县| 盐边县| 满城县| 孟津县| 金山区| 宁南县| 茌平县| 中宁县| 玉环县| 同江市| 新泰市| 嘉荫县| 天台县| 繁峙县| 白城市| 嘉善县| 仙居县| 株洲市| 尉氏县| 富顺县| 什邡市| 淮阳县| 全州县| 潮州市| 淮滨县| 拜泉县| 南投县| 长治县| 阿巴嘎旗| 定远县| 扬中市| 孟村|