
本教程将指导您如何使用javascript和html数据属性,构建一个包含两个独立点击计数器的页面,并实现一个全局总计功能。其中一个计数器每次点击使总计增加1,另一个计数器则按照设定的权重(例如每9次点击使总计增加1)来更新总计。我们将通过扩展现有的`clickcount`类和html配置来达到这一目标,确保代码模块化、易于维护。
教程背景与目标
在网页开发中,我们经常需要实现用户交互的计数功能。本教程将解决一个具体问题:在一个HTML页面上,存在两个独立的点击计数器,每个计数器都有自己的显示区域和重置按钮。在此基础上,我们需要添加一个全局的总计显示区域,该总计的更新规则如下:
- 当第一个按钮(例如“Verified Videos”)被点击时,全局总计直接增加1。
- 当第二个按钮(例如“Document Count”)的点击次数达到特定倍数(例如9、18、27等)时,全局总计增加1(即每9次点击增加1)。
为了实现这一目标,我们将对现有的JavaScript ClickCount 类进行扩展,并优化HTML结构以传递必要的配置信息。
HTML结构优化:引入配置参数
为了使每个计数器能够灵活地配置其对全局总计的影响,我们将利用HTML的data-*属性来传递额外信息。具体来说,我们将扩展 data-config-click 属性,增加 weight 和 resultElement 两个字段。
- weight: 用于指定该计数器对总计的权重。如果每次点击总计增加1,则 weight 为1。如果每N次点击总计增加1,则 weight 为N。
- resultElement: 用于指定全局总计显示元素的CSS选择器,以便JavaScript能够找到并更新它。
此外,我们需要一个专门的HTML元素来显示全局总计。
立即学习“Java免费学习笔记(深入)”;
更新后的 HTML (index.html):
Click counter Total Units = 0
关键变化说明:
- 两个 c-block div 中的 data-config-click 属性都增加了 "weight" 和 "resultElement" 字段。
- "Document Count" 计数器设置 weight: 9,表示每9次点击总计增加1。
- "Verified Videos" 计数器设置 weight: 1,表示每次点击总计增加1。
- resultElement: "#total-units" 指向了页面底部的总计显示元素。
- 页面底部添加了一个 span 元素,id="total-units",用于显示全局总计,并初始化为0。
CSS样式(保持不变)
CSS样式主要负责页面的布局和视觉呈现,对于本教程的功能实现没有直接影响,因此可以沿用原有的样式。
style.css (与原代码基本一致):
body {
background: #000000;
height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
.block-wrapper {
width: 100vw;
display: flex;
justify-content: center;
margin: auto;
}
.c-block {
margin: 50px;
}
button,
b {
display: block;
text-align: center;
}
button {
border: none;
cursor: pointer;
}
button:focus {
outline: none;
}
.doctile {
background-color: #000000;
margin-top: 40px;
width: 375px;
height: 425px;
border-radius: 12px;
border: solid;
border-color: rgb(170, 170, 170, 0.5);
display: flex;
justify-content: center;
margin: 20px;
}
.vidtile {
background-color: #000000;
margin-top: 40px;
width: 375px;
height: 425px;
border-radius: 12px;
border: solid;
border-color: rgb(170, 170, 170, 0.5);
display: flex;
justify-content: center;
margin: 20px;
}
.button {
margin-top: 40px;
color: rgb(0, 190, 255);
padding: 4px 10px;
border-radius: 6px;
border: solid;
border-color: rgb(170, 170, 170, 0.5);
font-size: 32px;
}
.button:hover {
opacity: 0.9;
}
.button[disabled] {
opacity: 0.7;
cursor: not-allowed;
}
.button-primary {
background-color: rgb(85, 85, 85, .4);
}
.button-secondary {
background-color: rgb(85, 85, 85, .4);
}
.button-reset {
background-color: rgb(85, 85, 85, .4);
display: block;
color: rgb(0, 190, 255);
margin: 12px auto 0;
font-size: 16px;
padding: 4px 56px;
border-radius: 6px;
border: solid;
border-width: 2px;
border-color: rgb(170, 170, 170, 0.5);
}
.result {
color: rgb(0, 190, 255);
font-size: 100px;
margin-bottom: 8px;
}
.totaltile {
width: 500px;
height: 50px;
padding: 4px 10px;
border-radius: 6px;
border: solid;
border-color: rgb(170, 170, 170, 0.5);
font-size: 32px;
display: flex;
justify-content: center;
margin: auto;
}
p {
color: rgb(0, 190, 255);
font-size: 25px;
margin-top: 10px;
}JavaScript逻辑实现:扩展ClickCount类
核心的逻辑修改将在 script.js 文件中完成。我们将扩展 ClickCount 类,使其能够处理全局总计的更新,并根据配置的 weight 值计算贡献。
更新后的 JavaScript (script.js):
window.onload = () => {
class ClickCount {
constructor(obj) {
this.triggerNode = obj.triggerNode;
this.targetNode = obj.targetNode;
this.resetNode = obj.resetNode;
this.weight = obj.weight; // 新增:权重
this.resultNode = obj.resultNode; // 新增:全局总计显示节点
this.count = 0;
this.handleClick();
this.handleCount();
}
/**
* 更新全局总计:增加当前计数器的贡献
* 逻辑:计算当前点击前后的加权贡献差值,并更新总计。
* 例如,如果weight=9,count从8到9,则加权贡献从0增加到1。
* 如果count从17到18,则加权贡献从1增加到2。
*/
increaseTotal() {
// 获取当前全局总计,如果非数字则默认为0
let currentTotal = isNaN(this.resultNode.innerHTML)
? 0
: parseInt(this.resultNode.innerHTML);
// 计算当前点击前,该计数器的加权贡献
const previousWeightedCount = parseInt(this.count / this.weight);
// 从总计中减去旧的加权贡献
currentTotal -= previousWeightedCount;
// 计算当前点击后(count + 1),该计数器的加权贡献
const newWeightedCount = parseInt((this.count + 1) / this.weight);
// 将新的加权贡献加回到总计中
currentTotal += newWeightedCount;
// 更新全局总计显示
this.resultNode.innerHTML = currentTotal;
}
/**
* 更新全局总计:减少当前计数器的贡献(用于重置)
* 逻辑:计算当前计数器的加权贡献,并从总计中减去。
*/
decreaseTotal() {
// 获取当前全局总计,如果非数字则默认为0
let currentTotal = isNaN(this.resultNode.innerHTML)
? 0
: parseInt(this.resultNode.innerHTML);
// 计算当前点击数对应的加权贡献
const weightedCount = parseInt(this.count / this.weight);
// 从总计中减去该贡献
currentTotal -= weightedCount;
// 更新全局总计显示
this.resultNode.innerHTML = currentTotal;
}
/**
* 处理点击事件
*/
handleClick() {
this.triggerNode.addEventListener("click", () => {
this.increaseTotal(); // 在计数器自身增加前,先更新全局总计
this.handleCount({
count: this.count++, // 增加计数器自身点击数
});
});
this.resetNode.addEventListener("click", () => this.handleReset());
}
/**
* 更新计数器自身的显示
*/
handleCount() {
this.targetNode.innerHTML = this.count;
}
/**
* 处理重置事件
*/
handleReset() {
this.decreaseTotal(); // 在计数器自身重置前,先从全局总计中移除贡献
this.count = 0; // 重置计数器自身点击数
this.handleCount();
}
}
// 遍历所有配置了点击计数器的HTML块
const clickBlock = document.querySelectorAll("[data-config-click]");
clickBlock.forEach((block) => {
const config = JSON.parse(block.dataset.configClick);
// 解构获取所有配置参数,包括新增的 weight 和 resultElement
const { trigger, target, reset, weight, resultElement } = config.selectors;
// 获取DOM节点
const triggerNode = block.querySelector(trigger);
const targetNode = block.querySelector(target);
const resetNode = block.querySelector(reset);
// 获取全局总计显示节点
const resultNode = document.querySelector(resultElement);
// 确保关键节点存在,然后创建 ClickCount 实例
if (triggerNode && targetNode) {
new ClickCount({
triggerNode,
targetNode,
resetNode,
resultNode, // 传递全局总计节点
weight, // 传递权重
});
}
});
};关键 JavaScript 变化说明:
- window.onload 包装: 将所有初始化代码放入 window.onload 事件监听器中,确保DOM完全加载后再执行脚本,避免因元素未加载而导致的错误。
-
ClickCount 构造函数扩展:
- 新增 this.weight 和 this.resultNode 属性,用于存储当前计数器的权重和全局总计的DOM节点。
-
increaseTotal() 方法:
- 这是实现加权总计的核心。它首先获取当前的全局总计。
- 然后,它计算当前点击前(this.count)该计数器对总计的贡献 (parseInt(this.count / this.weight)),并从 currentTotal 中减去。
- 接着,计算点击后(this.count + 1)该计数器对总计的贡献 (parseInt((this.count + 1) / this.weight)),并加回到 currentTotal 中。
- 这种“先减后加”的逻辑确保了即使连续多次调用 increaseTotal,只要 this.count 未改变,总计也不会重复增加。它准确地反映了在 this.count 增加1时,加权总计是否应该增加1。
- 最后,更新 resultNode 的 innerHTML。
-
decreaseTotal() 方法:
- 用于处理计数器重置时,从全局总计中移除其贡献。
- 它计算当前 this.count 对应的加权贡献,并直接从 currentTotal 中减去。
-
handleClick() 方法修改:
- 在 this.count++ 之前调用 this.increaseTotal(),确保在更新自身计数的同时,也更新全局总计。
-
handleReset() 方法修改:
- 在 this.count = 0 之前调用 this.decreaseTotal(),确保在重置自身计数的同时,也从全局总计中移除其贡献。
-
初始化逻辑修改:
- 在 clickBlock.forEach 循环中,从 config.selectors 中解构出 weight 和 resultElement。
- 使用 document.querySelector(resultElement) 获取全局总计的DOM节点 resultNode。
- 在创建 ClickCount 实例时,将 resultNode 和 weight 作为参数传递进去。
运行效果与注意事项
完成上述HTML、CSS和JavaScript的修改后,保存文件并在浏览器中打开 index.html。
- 点击“Verified Videos”按钮,其自身计数会增加,同时页面底部的“Total Units”也会增加1。
- 点击“Document Count”按钮,其自身计数会增加。每当其计数达到9、18、27等时,“Total Units”会增加1。
- 每个计数器的“Reset”按钮将重置该计数器,并同时从“Total Units”中减去该计数器之前贡献的加权值。
注意事项:
- DOM加载顺序: 使用 window.onload 是一个良好的实践,确保在JavaScript尝试访问DOM元素时,这些元素已经存在于文档中。
- 数据属性的解析: JSON.parse() 用于将 data-config-click 属性中的JSON字符串转换为JavaScript对象。请确保JSON格式正确,否则会导致解析错误。
- 全局总计的唯一性: resultElement 应该指向一个唯一的元素(例如通过 id 选择器),以便所有计数器都能更新同一个全局总计。
- 整数除法: parseInt(count / weight) 实现了向下取整的整数除法,这正是计算加权贡献所需的行为。
- 健壮性: 在 increaseTotal 和 decreaseTotal 方法中,通过 isNaN(this.resultNode.innerHTML) ? 0 : parseInt(this.resultNode.innerHTML) 来获取 currentTotal,增加了代码的健壮性,防止因 resultNode 内容非数字而导致的问题。
总结
通过本教程,我们成功地将两个独立的点击计数器与一个具有复杂加权规则的全局总计功能结合起来。我们利用HTML数据属性实现了灵活的配置,并通过扩展JavaScript类使其具备了管理全局总计的能力。这种模块化和可配置的设计模式在构建复杂的交互式网页应用时非常有用。










