You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实时截止日期倒计时功能实现求助:需结合DOM与LocalStorage

解决方案:实时截止倒计时 + DOM操作 + LocalStorage存储

现有代码的核心问题

  • 自定义元素DeadLine的update方法仅返回时间差数组,未更新DOM显示
  • 点击事件中获取用户输入的逻辑顺序错误(先清空输入框再取值,导致newTitle为空)
  • 时间计算方式有误(使用Date对象的getDate()会受时区影响,无法正确计算总天数)
  • 未实现LocalStorage数据持久化逻辑
  • 重复添加同一元素实例到DOM,导致渲染异常

完整修正代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>截止倒计时</title>
</head>
<body>
    <div>
        <input type="text" id="titleElem" placeholder="输入任务名称">
        <input type="datetime-local" id="timeElem">
        <button id="btn">添加倒计时</button>
    </div>
    <div id="deadlineElem"></div>

    <script>
        // 自定义倒计时元素
        class DeadLine extends HTMLElement {
            constructor() {
                super();
                // 创建shadow DOM隔离样式
                this.attachShadow({ mode: 'open' });
            }

            connectedCallback() {
                this.title = this.getAttribute('title') || '未命名任务';
                this.deadline = this.getAttribute('deadline');
                // 初始化渲染
                this.render();
                // 每秒更新一次
                this.intervalId = setInterval(() => this.update(), 1000);
            }

            // 计算剩余时间
            calculateRemainingTime() {
                const now = new Date().getTime();
                const deadlineTime = new Date(this.deadline).getTime();
                const diff = deadlineTime - now;

                if (diff <= 0) {
                    return { expired: true };
                }

                // 转换为天、时、分、秒
                const days = Math.floor(diff / (1000 * 60 * 60 * 24));
                const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
                const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
                const seconds = Math.floor((diff % (1000 * 60)) / 1000);

                return { days, hours, minutes, seconds, expired: false };
            }

            // 更新DOM显示
            update() {
                const timeData = this.calculateRemainingTime();
                this.render(timeData);
            }

            // 渲染元素内容
            render(timeData = null) {
                let timeHtml = '';
                if (timeData) {
                    if (timeData.expired) {
                        timeHtml = '<p style="color: red;">已过期</p>';
                    } else {
                        timeHtml = `<p>剩余:${timeData.days}天 ${timeData.hours}时 ${timeData.minutes}分 ${timeData.seconds}秒</p>`;
                    }
                }

                this.shadowRoot.innerHTML = `
                    <div style="border: 1px solid #ccc; padding: 10px; margin: 10px 0;">
                        <h3>${this.title}</h3>
                        ${timeHtml}
                        <button onclick="this.parentElement.parentElement.remove()">删除</button>
                    </div>
                `;
            }

            disconnectedCallback() {
                // 清除定时器,避免内存泄漏
                clearInterval(this.intervalId);
            }
        }

        customElements.define('my-deadline', DeadLine);

        // DOM元素引用
        const titleElem = document.getElementById('titleElem');
        const timeElem = document.getElementById('timeElem');
        const btn = document.getElementById('btn');
        const deadlineElem = document.getElementById('deadlineElem');

        // 从LocalStorage加载已保存的倒计时项
        function loadDeadlines() {
            const savedDeadlines = JSON.parse(localStorage.getItem('deadlines')) || [];
            savedDeadlines.forEach(item => {
                addDeadlineElement(item.title, item.deadline);
            });
        }

        // 添加单个倒计时元素到DOM
        function addDeadlineElement(title, deadline) {
            const deadlineItem = document.createElement('my-deadline');
            deadlineItem.setAttribute('title', title);
            deadlineItem.setAttribute('deadline', deadline);
            deadlineElem.appendChild(deadlineItem);
        }

        // 保存倒计时项到LocalStorage
        function saveDeadline(title, deadline) {
            const savedDeadlines = JSON.parse(localStorage.getItem('deadlines')) || [];
            savedDeadlines.push({ title, deadline });
            localStorage.setItem('deadlines', JSON.stringify(savedDeadlines));
        }

        // 监听删除事件,同步更新LocalStorage
        deadlineElem.addEventListener('click', (e) => {
            if (e.target.tagName === 'BUTTON') {
                const deadlineElement = e.target.closest('my-deadline');
                const title = deadlineElement.getAttribute('title');
                const deadline = deadlineElement.getAttribute('deadline');
                
                // 从LocalStorage移除对应项
                let savedDeadlines = JSON.parse(localStorage.getItem('deadlines')) || [];
                savedDeadlines = savedDeadlines.filter(item => !(item.title === title && item.deadline === deadline));
                localStorage.setItem('deadlines', JSON.stringify(savedDeadlines));
                
                deadlineElement.remove();
            }
        });

        // 点击添加按钮事件
        btn.addEventListener('click', () => {
            const title = titleElem.value.trim();
            const deadline = timeElem.value;

            if (!title || !deadline) {
                alert('请填写完整的任务名称和截止日期');
                return;
            }

            // 添加到DOM
            addDeadlineElement(title, deadline);
            // 保存到LocalStorage
            saveDeadline(title, deadline);

            // 清空输入框
            titleElem.value = '';
            timeElem.value = '';
        });

        // 页面加载时加载已保存的项
        window.addEventListener('load', loadDeadlines);
    </script>
</body>
</html>

关键功能说明

  1. 自定义元素优化

    • 使用Shadow DOM隔离组件样式,避免全局样式冲突
    • connectedCallback初始化属性并启动定时器,disconnectedCallback清除定时器防止内存泄漏
    • 拆分calculateRemainingTime和render方法,逻辑更清晰
  2. 时间计算修复

    • 通过时间戳计算差值,避免时区影响
    • 处理截止日期过期的情况,显示红色提示
  3. LocalStorage持久化

    • loadDeadlines:页面加载时读取本地存储的任务并渲染
    • saveDeadline:新增任务时保存到本地存储
    • 监听删除事件,同步删除本地存储中的对应项
  4. DOM操作优化

    • 正确获取用户输入(先取值再清空输入框)
    • 统一封装addDeadlineElement方法创建自定义元素
    • 为每个倒计时项添加删除按钮,点击后同步更新DOM和LocalStorage

内容的提问来源于stack exchange,提问作者gnehgo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 14:47:48