飞流 发表于 2025-9-18 19:34:25

给你的网页加上一个优雅的弹窗公告

引用一个JS文件来实现任意页面弹窗公告,弹窗公告代码JS文件本地化可放心使用。
效果展示

公告能够在不同设备自适应显示实现方法1.在网站目录创建文件 itjs.js ,并且粘贴以下代码到你刚刚创建的这个JS文件里面。
// 公告弹窗
(function () {
    function showAnnounce() {
      const lastClose = localStorage.getItem('announceLastClose');
      if (!lastClose) return true;
      const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
      return new Date(lastClose).getTime() < oneDayAgo;
    }<p></p>
<p style="line-height: 30px; text-indent: 2em;">    if (!showAnnounce()) return;</p>
<p style="line-height: 30px; text-indent: 2em;">    const cfg = window.announceConfig || {
      title: "公告通知",
      items: [
            {
                icon: "news",
                title: "最新公告",
                content: "本平台新增多项安全验证功能,建议您及时完善个人信息。"
            },
            {
                icon: "update",
                title: "功能更新",
                content: "新版移动端已上线,支持离线模式和数据同步功能。"
            }
      ],
      noShowText: "一天内不再显示",
      laterText: "稍后提醒",
      okText: "我知道了",
      mainColor: "#3b82f6",
      maxW: "500px",
      z: 9999
    };</p>
<p style="line-height: 30px; text-indent: 2em;">    function addStyles() {
      const style = document.createElement('style');
      style.textContent = `
            .announce-mask {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0,0,0,0.5);
                z-index: ${cfg.z - 1};
                opacity: 0;
                visibility: hidden;
                transition: opacity 0.3s;
                backdrop-filter: blur(4px);
            }
            
            .announce-mask.show {
                opacity: 1;
                visibility: visible;
            }
            
            .announce-box {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%) scale(0.95);
                background: white;
                border-radius: 10px;
                max-width: ${cfg.maxW};
                width: calc(100% - 30px);
                box-shadow: 0 5px 20px rgba(0,0,0,0.1);
                z-index: ${cfg.z};
                overflow: hidden;
                opacity: 0;
                visibility: hidden;
                transition: all 0.3s;
            }
            
            .announce-box.show {
                opacity: 1;
                visibility: visible;
                transform: translate(-50%, -50%) scale(1);
            }
            
            .announce-head {
                background: ${cfg.mainColor};
                color: white;
                padding: 15px 20px;
                display: flex;
                align-items: center;
                justify-content: space-between;
            }
            
            .announce-title {
                margin: 0;
                font-size: 1.2rem;
                display: flex;
                align-items: center;
                gap: 10px;
            }
            
            .announce-close {
                background: none;
                border: none;
                color: white;
                font-size: 1.5rem;
                cursor: pointer;
                width: 30px;
                height: 30px;
                display: flex;
                align-items: center;
                justify-content: center;
                border-radius: 50%;
            }
            
            .announce-close:hover {
                background: rgba(255,255,255,0.2);
            }
            
            .announce-body {
                padding: 20px;
            }
            
            .announce-list {
                margin: 0;
                padding: 0;
                list-style: none;
            }
            
            .announce-item {
                display: flex;
                gap: 12px;
                padding: 15px 0;
                border-bottom: 1px solid #f1f5f9;
            }
            
            .announce-item:last-child {
                border-bottom: none;
                padding-bottom: 0;
            }
            
            .announce-icon {
                width: 40px;
                height: 40px;
                border-radius: 8px;
                display: flex;
                align-items: center;
                justify-content: center;
                flex-shrink: 0;
            }
            
            .announce-icon.news {
                background: #e0f2fe;
            }
            
            .announce-icon.update {
                background: #e6f7ee;
            }
            
            .announce-icon svg {
                width: 20px;
                height: 20px;
            }
            
            .announce-icon.news svg {
                fill: #0284c7;
            }
            
            .announce-icon.update svg {
                fill: #059669;
            }
            
            .announce-text {
                flex: 1;
            }
            
            .announce-item-title {
                margin: 0 0 5px 0;
                font-size: 1rem;
                font-weight: 600;
                color: #1e293b;
            }
            
            .announce-item-desc {
                margin: 0;
                font-size: 0.9rem;
                color: #64748b;
                line-height: 1.5;
            }
            
            .announce-option {
                padding: 0 20px 15px;
            }
            
            .announce-check {
                display: flex;
                align-items: center;
                gap: 8px;
                cursor: pointer;
            }
            
            .announce-checkbox {
                width: 18px;
                height: 18px;
                accent-color: ${cfg.mainColor};
            }
            
            .announce-check-label {
                font-size: 0.9rem;
                color: #64748b;
            }
            
            .announce-foot {
                padding: 15px 20px;
                border-top: 1px solid #f1f5f9;
                display: flex;
                gap: 10px;
            }
            
            .announce-btn {
                padding: 9px 15px;
                border: none;
                border-radius: 6px;
                font-size: 0.9rem;
                cursor: pointer;
                flex: 1;
                transition: all 0.2s;
            }
            
            .announce-btn.later {
                background: #f1f5f9;
                color: #475569;
            }
            
            .announce-btn.later:hover {
                background: #e2e8f0;
            }
            
            .announce-btn.ok {
                background: ${cfg.mainColor};
                color: white;
            }
            
            .announce-btn.ok:hover {
                background: ${shadeColor(cfg.mainColor, -15)};
            }
            
            @media (max-width: 480px) {
                .announce-foot {
                  flex-direction: column;
                }
            }
      `;
      document.head.appendChild(style);
    }</p>
<p style="line-height: 30px; text-indent: 2em;">    function shadeColor(color, percent) {
      let R = parseInt(color.substring(1, 3), 16);
      let G = parseInt(color.substring(3, 5), 16);
      let B = parseInt(color.substring(5, 7), 16);</p>
<p style="line-height: 30px; text-indent: 2em;">      R = parseInt(R * (100 + percent) / 100);
      G = parseInt(G * (100 + percent) / 100);
      B = parseInt(B * (100 + percent) / 100);</p>
<p style="line-height: 30px; text-indent: 2em;">      R = R < 255 ? R : 255;
      G = G < 255 ? G : 255;
      B = B < 255 ? B : 255;</p>
<p style="line-height: 30px; text-indent: 2em;">      R = Math.round(R);
      G = Math.round(G);
      B = Math.round(B);</p>
<p style="line-height: 30px; text-indent: 2em;">      const RR = (R.toString(16).length === 1) ? "0" + R.toString(16) : R.toString(16);
      const GG = (G.toString(16).length === 1) ? "0" + G.toString(16) : G.toString(16);
      const BB = (B.toString(16).length === 1) ? "0" + B.toString(16) : B.toString(16);</p>
<p style="line-height: 30px; text-indent: 2em;">      return "#" + RR + GG + BB;
    }</p>
<p style="line-height: 30px; text-indent: 2em;">    function closeAnnounce(mask, box, noShow) {
      mask.classList.remove('show');
      box.classList.remove('show');</p>
<p style="line-height: 30px; text-indent: 2em;">      if (noShow) {
            localStorage.setItem('announceLastClose', new Date().toISOString());
      }</p>
<p style="line-height: 30px; text-indent: 2em;">      setTimeout(() => {
            mask.remove();
            box.remove();
      }, 300);
    }</p>
<p style="line-height: 30px; text-indent: 2em;">    function createItems(container) {
      const list = document.createElement('ul');
      list.className = 'announce-list';</p>
<p style="line-height: 30px; text-indent: 2em;">      cfg.items.forEach(item => {
            const li = document.createElement('li');
            li.className = 'announce-item';</p>
<p style="line-height: 30px; text-indent: 2em;">            const icon = document.createElement('div');
            icon.className = `announce-icon ${item.icon}`;</p>
<p style="line-height: 30px; text-indent: 2em;">            let svg = '';
            if (item.icon === 'news') {
                svg = `
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM7 7h5v5H7zm5 7H7v2h5zm2-4h2v2h-2zm0 4h2v2h-2z"/>
                  </svg>
                `;
            } else if (item.icon === 'update') {
                svg = `
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8zm-2 8v3l4-4-4-4v3H6v2h4z"/>
                  </svg>
                `;
            }</p>
<p style="line-height: 30px; text-indent: 2em;">            icon.innerHTML = svg;</p>
<p style="line-height: 30px; text-indent: 2em;">            const text = document.createElement('div');
            text.className = 'announce-text';</p>
<p style="line-height: 30px; text-indent: 2em;">            const title = document.createElement('h4');
            title.className = 'announce-item-title';
            title.textContent = item.title;</p>
<p style="line-height: 30px; text-indent: 2em;">            const desc = document.createElement('p');
            desc.className = 'announce-item-desc';
            desc.textContent = item.content;</p>
<p style="line-height: 30px; text-indent: 2em;">            text.appendChild(title);
            text.appendChild(desc);</p>
<p style="line-height: 30px; text-indent: 2em;">            li.appendChild(icon);
            li.appendChild(text);
            list.appendChild(li);
      });</p>
<p style="line-height: 30px; text-indent: 2em;">      container.appendChild(list);
    }</p>
<p style="line-height: 30px; text-indent: 2em;">    function createAnnounce() {
      const mask = document.createElement('div');
      mask.className = 'announce-mask';</p>
<p style="line-height: 30px; text-indent: 2em;">      const box = document.createElement('div');
      box.className = 'announce-box';</p>
<p style="line-height: 30px; text-indent: 2em;">      const head = document.createElement('div');
      head.className = 'announce-head';</p>
<p style="line-height: 30px; text-indent: 2em;">      const title = document.createElement('h3');
      title.className = 'announce-title';
      title.innerHTML = `
            <svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor">
                <path d="M12 2c5.51 0 10 4.49 10 10s-4.49 10-10 10S2 17.51 2 12 6.49 2 12 2zm0 2c-4.41 0-8 3.59-8 8s3.59 8 8 8 8-3.59 8-8-3.59-8-8-8zm-1 3h2v6h-2zm0 8h2v2h-2z"/>
            </svg>
            ${cfg.title}
      `;</p>
<p style="line-height: 30px; text-indent: 2em;">      const closeBtn = document.createElement('button');
      closeBtn.className = 'announce-close';
      closeBtn.textContent = '×';
      closeBtn.addEventListener('click', () => {
            closeAnnounce(mask, box, false);
      });</p>
<p style="line-height: 30px; text-indent: 2em;">      head.appendChild(title);
      head.appendChild(closeBtn);</p>
<p style="line-height: 30px; text-indent: 2em;">      const body = document.createElement('div');
      body.className = 'announce-body';
      createItems(body);</p>
<p style="line-height: 30px; text-indent: 2em;">      const option = document.createElement('div');
      option.className = 'announce-option';</p>
<p style="line-height: 30px; text-indent: 2em;">      const check = document.createElement('label');
      check.className = 'announce-check';</p>
<p style="line-height: 30px; text-indent: 2em;">      const checkbox = document.createElement('input');
      checkbox.type = 'checkbox';
      checkbox.className = 'announce-checkbox';
      checkbox.id = 'noShow';</p>
<p style="line-height: 30px; text-indent: 2em;">      const label = document.createElement('span');
      label.className = 'announce-check-label';
      label.textContent = cfg.noShowText;</p>
<p style="line-height: 30px; text-indent: 2em;">      check.appendChild(checkbox);
      check.appendChild(label);
      option.appendChild(check);</p>
<p style="line-height: 30px; text-indent: 2em;">      const foot = document.createElement('div');
      foot.className = 'announce-foot';</p>
<p style="line-height: 30px; text-indent: 2em;">      const laterBtn = document.createElement('button');
      laterBtn.className = 'announce-btn later';
      laterBtn.textContent = cfg.laterText;
      laterBtn.addEventListener('click', () => {
            closeAnnounce(mask, box, false);
      });</p>
<p style="line-height: 30px; text-indent: 2em;">      const okBtn = document.createElement('button');
      okBtn.className = 'announce-btn ok';
      okBtn.textContent = cfg.okText;
      okBtn.addEventListener('click', () => {
            closeAnnounce(mask, box, checkbox.checked);
      });</p>
<p style="line-height: 30px; text-indent: 2em;">      foot.appendChild(laterBtn);
      foot.appendChild(okBtn);</p>
<p style="line-height: 30px; text-indent: 2em;">      box.appendChild(head);
      box.appendChild(body);
      box.appendChild(option);
      box.appendChild(foot);</p>
<p style="line-height: 30px; text-indent: 2em;">      document.body.appendChild(mask);
      document.body.appendChild(box);</p>
<p style="line-height: 30px; text-indent: 2em;">      setTimeout(() => {
            mask.classList.add('show');
            box.classList.add('show');
      }, 100);</p>
<p style="line-height: 30px; text-indent: 2em;">      mask.addEventListener('click', (e) => {
            if (e.target === mask) {
                closeAnnounce(mask, box, false);
            }
      });</p>
<p style="line-height: 30px; text-indent: 2em;">      document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                closeAnnounce(mask, box, false);
            }
      });
    }</p>
<p style="line-height: 30px; text-indent: 2em;">    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => {
            addStyles();
            createAnnounce();
      });
    } else {
      addStyles();
      createAnnounce();
    }
})();</p>
这里的文本按照你的需求进行更改,其中 icon 为图标名称,小白切勿随意修改,否则图标将无法正常显示。
2.在需要弹窗公告的页面加上以下代码即可实现弹窗公告功能。一般加在主页,也可以加在全局文件实现全部页面弹出公告,具体根据你的实际需求。<script src="itjs.js"></script>小白请注意:src后的路径根据实际情况调整!

关于这段代码放在页面哪里比较好:1.body标签的末尾(推荐)注意:不是body标签后面!在body标签里面,放在</body>标签前。

优点:此时页面 DOM 加载完成后再执行公告脚本,避免因 DOM 未就绪导致的问题,同时不阻塞页面渲染
缺点:公告弹窗会比放在head中稍晚出现
2.head标签内,配合defer属性<script src="itjs.js" defer></script>优点:提前加载脚本,但会等待 DOM 就绪后再执行,不阻塞页面渲染
缺点:需要浏览器支持defer属性(现代浏览器都支持)
3.head标签内,不使用任何属性(不推荐)
缺点:会阻塞页面渲染,直到脚本加载并执行完成,影响页面加载速度
综合考虑,推荐放在body标签末尾,这种方式既能保证脚本正常运行,又不会影响页面的加载性能,公告弹窗会在页面内容渲染完成后适时出现,给用户更好的体验。
如果希望公告尽可能早地出现,但又不影响页面加载,可以选择第二种方式(head中加defer)
代码说明模块化设计:采用立即执行函数表达式 (IIFE) 封装,避免全局变量污染,内部功能划分清晰(样式添加、元素创建、事件处理等)响应式设计:在移动设备上自动调整布局,支持点击遮罩层、关闭按钮、ESC 键等多种关闭方式。样式隔离:公告代码通过独特的类名前缀(announce-)避免样式冲突。有任何问题可在评论区说明,博主随时在线,若你实在不会操作可以联系博主为你操作(有偿)。

波斯虎 发表于 2025-9-26 00:22:01

{:144:}            
页: [1]
查看完整版本: 给你的网页加上一个优雅的弹窗公告