引用一个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;
}
if (!showAnnounce()) return;
const cfg = window.announceConfig || {
title: "公告通知",
items: [
{
icon: "news",
title: "最新公告",
content: "本平台新增多项安全验证功能,建议您及时完善个人信息。"
},
{
icon: "update",
title: "功能更新",
content: "新版移动端已上线,支持离线模式和数据同步功能。"
}
],
noShowText: "一天内不再显示",
laterText: "稍后提醒",
okText: "我知道了",
mainColor: "#3b82f6",
maxW: "500px",
z: 9999
};
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);
}
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);
R = parseInt(R * (100 + percent) / 100);
G = parseInt(G * (100 + percent) / 100);
B = parseInt(B * (100 + percent) / 100);
R = R < 255 ? R : 255;
G = G < 255 ? G : 255;
B = B < 255 ? B : 255;
R = Math.round(R);
G = Math.round(G);
B = Math.round(B);
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);
return "#" + RR + GG + BB;
}
function closeAnnounce(mask, box, noShow) {
mask.classList.remove('show');
box.classList.remove('show');
if (noShow) {
localStorage.setItem('announceLastClose', new Date().toISOString());
}
setTimeout(() => {
mask.remove();
box.remove();
}, 300);
}
function createItems(container) {
const list = document.createElement('ul');
list.className = 'announce-list';
cfg.items.forEach(item => {
const li = document.createElement('li');
li.className = 'announce-item';
const icon = document.createElement('div');
icon.className = `announce-icon ${item.icon}`;
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>
`;
}
icon.innerHTML = svg;
const text = document.createElement('div');
text.className = 'announce-text';
const title = document.createElement('h4');
title.className = 'announce-item-title';
title.textContent = item.title;
const desc = document.createElement('p');
desc.className = 'announce-item-desc';
desc.textContent = item.content;
text.appendChild(title);
text.appendChild(desc);
li.appendChild(icon);
li.appendChild(text);
list.appendChild(li);
});
container.appendChild(list);
}
function createAnnounce() {
const mask = document.createElement('div');
mask.className = 'announce-mask';
const box = document.createElement('div');
box.className = 'announce-box';
const head = document.createElement('div');
head.className = 'announce-head';
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}
`;
const closeBtn = document.createElement('button');
closeBtn.className = 'announce-close';
closeBtn.textContent = '×';
closeBtn.addEventListener('click', () => {
closeAnnounce(mask, box, false);
});
head.appendChild(title);
head.appendChild(closeBtn);
const body = document.createElement('div');
body.className = 'announce-body';
createItems(body);
const option = document.createElement('div');
option.className = 'announce-option';
const check = document.createElement('label');
check.className = 'announce-check';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'announce-checkbox';
checkbox.id = 'noShow';
const label = document.createElement('span');
label.className = 'announce-check-label';
label.textContent = cfg.noShowText;
check.appendChild(checkbox);
check.appendChild(label);
option.appendChild(check);
const foot = document.createElement('div');
foot.className = 'announce-foot';
const laterBtn = document.createElement('button');
laterBtn.className = 'announce-btn later';
laterBtn.textContent = cfg.laterText;
laterBtn.addEventListener('click', () => {
closeAnnounce(mask, box, false);
});
const okBtn = document.createElement('button');
okBtn.className = 'announce-btn ok';
okBtn.textContent = cfg.okText;
okBtn.addEventListener('click', () => {
closeAnnounce(mask, box, checkbox.checked);
});
foot.appendChild(laterBtn);
foot.appendChild(okBtn);
box.appendChild(head);
box.appendChild(body);
box.appendChild(option);
box.appendChild(foot);
document.body.appendChild(mask);
document.body.appendChild(box);
setTimeout(() => {
mask.classList.add('show');
box.classList.add('show');
}, 100);
mask.addEventListener('click', (e) => {
if (e.target === mask) {
closeAnnounce(mask, box, false);
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeAnnounce(mask, box, false);
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
addStyles();
createAnnounce();
});
} else {
addStyles();
createAnnounce();
}
})();
这里的文本按照你的需求进行更改,其中 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-)避免样式冲突。
有任何问题可在评论区说明,博主随时在线,若你实在不会操作可以联系博主为你操作(有偿)。
此代码由IT技术博客原创,转载请说明。
没有回复内容