
在网页开发中,我们经常需要展示动态变化的数值,例如商品库存、活动剩余名额等。当页面上只有一个这样的计数器时,使用简单的JavaScript配合DOM元素的ID属性即可实现。然而,当需要在同一页面上展示多个独立的计数器时,传统方法往往会遇到挑战。
最初的实现方式可能如下所示,它通过一个全局的<span id="qty">元素和一个脚本来管理库存:
<span class="qty" id="qty"></span>
<script>
const setQty = (qty) => {
qtySpan.innerHTML = qty;
if (qty == 0) return;
let parts = Math.floor((Math.random() * 3) + 1);
if (parts > qty) parts = qty;
const msec = Math.floor(((Math.random() * 15) + 15) * 1000);
qty -= parts;
// Save the updated quantity in localStorage
localStorage.setItem('saved_countdown', qty);
setTimeout(() => setQty(qty), msec);
}
// Get the saved countdown value from localStorage, or use default value of 57 if not found
const defaultQty = localStorage.getItem('saved_countdown') ?? 57;
const qtySpan = document.getElementById('qty');
// Set the initial value of the quantity
setQty(defaultQty);
</script>这种方法在只有一个计数器时工作良好。但当尝试通过复制脚本并更改ID(如qty1, qty2等)来创建多个计数器时,会发现只有第一个或某一个计数器能够正常工作。其根本原因在于:
为了解决这些问题,我们需要一种更模块化、更具封装性的方法来创建可复用的UI组件,而Web Components中的自定义元素(Custom Elements)正是为此而生。
自定义元素允许我们定义自己的HTML标签,并为其关联特定的JavaScript行为和样式。通过这种方式,每个计数器都可以成为一个独立的组件,拥有自己的内部状态和逻辑,互不干扰。
我们将创建一个名为<stock-counter>的自定义元素,它将具备以下特性:
以下是<stock-counter>自定义元素的完整实现:
customElements.define('stock-counter', class extends HTMLElement {
// quantity 属性的 getter 方法
get quantity() {
// 1. 检查是否设置了 storage-key 并且 localStorage 中有存储的值
if (this.storageKey !== null) {
const value = Number(localStorage.getItem(this.storageKey));
// 如果存储的值是有效的数字且不为 0,则优先使用它
if (!Number.isNaN(value) && value !== 0) {
return value;
}
}
// 2. 如果没有有效的存储值,则从 quantity 属性获取初始值
const value = Number(this.getAttribute('quantity'));
// 如果 quantity 属性值无效,则返回 0
if (Number.isNaN(value)) {
return 0;
}
return value;
}
// quantity 属性的 setter 方法
set quantity(value) {
if (!isNaN(value)) {
// 如果设置了 storage-key,则将新值存储到 localStorage
if (this.storageKey !== null) {
localStorage.setItem(this.storageKey, value);
}
// 更新元素的 quantity 属性
this.setAttribute('quantity', value);
}
}
// storage-key 属性的 getter 方法
get storageKey() {
return this.getAttribute('storage-key');
}
// 当元素被添加到文档流时调用
connectedCallback() {
this.count(); // 启动计数器
}
// 核心计数逻辑方法
count = () => {
const qty = this.quantity; // 获取当前数量
this.textContent = qty; // 更新元素显示文本
if (qty === 0) {
return; // 数量为 0 时停止计数
}
let parts = Math.floor((Math.random() * 3) + 1); // 随机生成递减数量 (1-3)
if (parts > qty) {
parts = qty; // 确保递减数量不超过当前数量
}
this.quantity -= parts; // 递减数量,同时触发 setter 更新 localStorage 和属性
const msec = Math.floor(((Math.random() * 15) + 15) * 1000); // 随机生成下一次递减的间隔时间 (15-30秒)
setTimeout(this.count, msec); // 在指定时间后再次调用 count 方法
};
});customElements.define('stock-counter', class extends HTMLElement { ... });
quantity getter/setter
storageKey getter
connectedCallback()
count = () => { ... };
一旦自定义元素被定义,你就可以像使用任何标准HTML标签一样在页面中使用它。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多库存计数器示例</title>
<style>
stock-counter {
display: inline-block; /* 使其表现得像行内块元素 */
padding: 5px 10px;
margin: 10px;
border: 1px solid #ccc;
background-color: #f9f9f9;
font-size: 1.2em;
font-weight: bold;
min-width: 50px;
text-align: center;
}
</style>
</head>
<body>
<h1>产品库存状态</h1>
<div>
产品A库存: <stock-counter quantity="40" storage-key="countdown-product-a"></stock-counter>
</div>
<div>
产品B库存: <stock-counter quantity="50" storage-key="countdown-product-b"></stock-counter>
</div>
<div>
产品C库存: <stock-counter quantity="80" storage-key="countdown-product-c"></stock-counter>
</div>
<div>
产品D库存 (无持久化): <stock-counter quantity="25"></stock-counter>
</div>
<!-- 将自定义元素的定义脚本放在 body 结束标签前,确保 DOM 元素已加载 -->
<script>
customElements.define('stock-counter', class extends HTMLElement {
get quantity() {
if (this.storageKey !== null) {
const value = Number(localStorage.getItem(this.storageKey));
if (!Number.isNaN(value) && value !== 0) {
return value;
}
}
const value = Number(this.getAttribute('quantity'));
if (Number.isNaN(value)) {
return 0;
}
return value;
}
set quantity(value) {
if (!isNaN(value)) {
if (this.storageKey !== null) {
localStorage.setItem(this.storageKey, value);
}
this.setAttribute('quantity', value);
}
}
get storageKey() {
return this.getAttribute('storage-key');
}
connectedCallback() {
this.count();
}
count = () => {
const qty = this.quantity;
this.textContent = qty;
if (qty === 0) {
return;
}
let parts = Math.floor((Math.random() * 3) + 1);
if (parts > qty) {
parts = qty;
}
this.quantity -= parts;
const msec = Math.floor(((Math.random() * 15) + 15) * 1000);
setTimeout(this.count, msec);
};
});
</script>
</body>
</html>在上面的示例中:
通过采用自定义元素,我们成功地将复杂的、相互独立的计数器逻辑封装成可复用的HTML标签。这种方法不仅解决了传统脚本在多实例场景下的问题,还极大地提升了代码的模块化、可读性和可维护性,是构建现代Web应用程序的强大工具。
以上就是利用自定义元素实现页面多处独立库存计数器的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号