使用方法
- 「每步计数」填每按一次加多少,合法区间 1 到 999,默认 1。想把两个人各点一次合成一步就填 2(示例值 2 出现在下方对照表第 7 行)。
- 点「+ 加一步」累计加一步长,点「− 减一步」累计减一步长,减成负数不报错,累计会如实显示负值。
- 点错了就按「撤销上一步」,撤销会同时从逐次记录里删掉对应一行;「清零重来」把累计和记录全部归零。
- 在「每步计数」输入框里直接按回车,等于点一次「+ 加一步」,不用鼠标就能连续计次。
- 右侧逐次记录默认显示最近 12 条,点「加载更多」每次加 12 条;点「导出记录 CSV」得到带 BOM 的文件,Excel 打开中文不乱码。第一次用没有记录时,点「载入示例」可以看到点击统计计数统计的 12 条演示数据,累计 8。
点击统计计数统计能解决什么问题
最直接的用法是数东西:数车厢里进来多少人、数仓库点了多少箱货、数这页样稿被翻了多少次。纸笔计数器掉地上就错位,手机上装 App 又要注册登录,点击统计计数统计打开就用,按一下加一下,误触还能撤销。
第二个场景是人工抽样清点。质检要数不良品数量,有时数到一半发现前面多数了 3 个,用减一步或连续撤销把累计退回去,逐次记录里每一步的时间都留着,事后能对上哪一段数错了。
第三个场景是给「次数」留档。习惯打卡、跳绳个数、拨打电话次数这类只有次数没有金额的事,用导出 CSV 把当天记录存成文件,比截图可靠。需要说明口径:本页是本地计次,不是网站埋点统计——它不会采集访客数据、没有 PV/UV 概念、也不向任何服务器上报;要测手速快慢有专门的点击速度测试每秒点击页,要多人比分排名有在线计分板,这两件事都不在本页。
原理与规则:localStorage、JSON 序列化与 storage 事件
本页的核心函数只有四个:localStorage.getItem、localStorage.setItem、JSON.parse、JSON.stringify。每按一次按钮,程序做三件事:把累计值加上步长得到新累计;往日志数组末尾推一条 { "t": 时间戳, "d": 步长, "c": 新累计 };把 { c: 累计, s: 步长, log: 日志 } 整体 JSON.stringify 后写入键名 lq-tally-counter-v1。刷新页面后 JSON.parse 还原整个状态,这就是「刷新不丢」的全部原理。
存储用的 Web Storage API 在 HTML Living Standard 的 webstorage 一节定义:localStorage 按源(协议 + 域名 + 端口)隔离,写入是同步的,数据持久保留,直到你清除站点数据。本页没有任何 fetch、XMLHttpRequest 或图片打点请求,计数全程不离开浏览器,可以在开发者工具的 Network 面板里核实:点十次按钮,网络请求是零。
多标签页同步靠 storage 事件。你在 A 标签页按加号,浏览器会在同源的其他标签页触发 storage 事件,本页监听它并重新读取计数——所以开两个窗口也能看到数字同步。但要清楚它的限制:storage 事件只带最终值,不带「谁加了几步」的过程,两个标签页同时按按钮时是各自「读出旧值、加步长、写回」,后写入的把先写入的覆盖掉,这就是典型的 last-writer-wins 竞态。计次场景损失的是个别几次计数,可以接受;如果拿这套思路去写多人实时协作,就必须换成服务端序列化,localStorage 做不到。
日志条目的时间戳用 new Date().toISOString() 生成,形如 2026-10-03T09:30:15.120Z,这是 ISO 8601 格式,末尾的 Z 表示 UTC 时区。展示时只截取时分秒给你看,导出 CSV 时保留完整 ISO 串,避免时区歧义——「09:30」到底是上午九点半还是 UTC 的九点半,看完整串就不会吵。
完整示例(数值仅作演示)
点「载入示例」后得到的 12 条记录如下(时间用 UTC 显示,累计从 0 开始):
- 09:00:00 到 09:02:15 连加 4 次 +1,累计到 3
- 09:03:00 起再连加两次 +1,累计 5;09:04:30 一次 +2(把「每步计数」改成 2 再改回 1 的效果),累计 7
- 09:07:30 一次 −2,累计从 9 退到 7;最后一次 09:08:15 加 +1,最终累计 8
- 校验:正步合计 +12,负步合计 −4,12 − 4 = 8,与显示一致
这个 8 会出现在累计大字、逐次记录最后一行、以及导出 CSV 的最后一行,三处咬合。撤销一次 −2 那步,累计回到 9,记录剩 11 条——撤销不是「再减回去」,而是把那一步从历史里拿掉,所以正负合计校验依然成立。
输入范围与对照表
「每步计数」的合法区间是 1 到 999 的整数,输入框已设 min="1" max="999" step="1"。下表是载入示例后的完整 12 行演示数据:
| 时间(UTC) | 步长 | 累计 |
|---|---|---|
| 09:00:00 | +1 | 1 |
| 09:00:45 | +1 | 2 |
| 09:01:30 | +1 | 3 |
| 09:02:15 | -1 | 2 |
| 09:03:00 | +1 | 3 |
| 09:03:45 | +1 | 4 |
| 09:04:30 | +2 | 6 |
| 09:05:15 | +1 | 7 |
| 09:06:00 | +1 | 8 |
| 09:06:45 | +1 | 9 |
| 09:07:30 | -2 | 7 |
| 09:08:15 | +1 | 8 |
导出 CSV 的表头是「时间,步长,累计」,数据行与本表逐行一致,文件以 UTF-8 BOM(U+FEFF)开头,Excel 双击打开中文表头不乱码。想要批量补录历史次数,可以把步长设成较大值一次到位:比如累计 500 次只差 380,把每步改成 380 加一次再改回 1,日志里会如实记下这次 +380。
输入范围、边界与精度说明
计数上限 999999999。加到 10^9−1 再按加号会明确报错而不是继续加。这个上限远低于 JavaScript 的安全整数极限 Number.MAX_SAFE_INTEGER = 2^53 − 1 = 9007199254740991(16 位),两者差约 900 万倍,所以本页所有加减都是精确整数运算,不存在浮点误差;设 9 位上限是为了让累计大字在任何手机屏上都不换行,纯粹是显示考虑。
日志只保留最近 2000 条,更早的会被截断。按单条日志序列化后 46 字符(UTF-16 占 92 字节)算,2000 条约 180 KB,离 localStorage 满配额(多数浏览器 5 MiB = 5242880 字节)还很远,但足以覆盖一整天的连续计次。截断是刻意的:如果永远不清,长期用户的存储会无限膨胀,直到某天 setItem 抛 QuotaExceededError 才发现。撤销也只在这 2000 条里有效,最早的记录被截掉后就没有可撤销的对象了。
隐私模式与存储被禁:Safari 无痕模式等场景下 localStorage 可能抛异常,本页 setItem 全部包在 try/catch 里,失败时计数照常进行,但会给出提示「本次只在内存中,刷新后清零」——工具降级可用,而不是默默丢数据。
常见误用一:把点击统计计数统计当埋点统计。本页没有页面曝光、访客去重、渠道来源这些字段,也不会上报任何数据;它是给自己数数用的,不是给网站做流量分析用的。
常见误用二:两个标签页各点一半,期待结果相加。如上节所说,同时写入是 last-writer-wins,结果会丢几次;正确做法是只在一个标签页里计,或者分两段导出 CSV 后在表格软件里相加。
常见误用三:清浏览器缓存后发现计数没了。localStorage 属于「站点数据」,「清除浏览数据」里的 Cookie 与站点数据选项会把它一起清掉;想保底就导出 CSV,文件在你手里才叫留档。
常见问题
计数会自动保存吗?关掉浏览器再打开还在吗?
在。每次按键都即时写入 localStorage,不是关页面前才保存,所以断电、崩溃都不会丢最后几次。但它的存活范围是「同一个浏览器的同一个源」:换浏览器、换设备、用无痕窗口、或点了清除站点数据,都会从 0 开始。要把数字带去别的设备,用导出 CSV。
为什么减一步可以减成负数?计数器不应该是正数吗?
机械计数器确实只能加,但人工清点最常见的操作就是「多点了退回去」,如果减到底就报错,你会被迫清零重来。本页把负值如实显示,配合撤销功能,正好覆盖补录和退账两种用法。累计下限也是 −999999999,与上限对称。
同一个网站开两个标签页,两边数字会打架吗?
会看到同步(本页监听了 storage 事件),但同时按键会互相覆盖——两边各自「读旧值、加步长、写回」,后写的赢。偶尔错开按键没问题,密集并行就丢次数。最稳的用法是固定一个标签页计次,另一个只看不按。
日志为什么只有 2000 条?能再大点吗?
2000 条约 180 KB,是为了让长期用户永远碰不到 5 MiB 配额上限而设的安全值,同时也把撤销的有效范围控制在一天内的高频使用量。超过 2000 条后最新的照常记录,最早的被挤出;真需要完整历史就当天导出 CSV,别依赖页面里无限堆记录。
每步计数最大 999,我想一次加 5000 怎么办?
分五次、每次 1000?不行,上限就是 999。设计原因是步长输入框要同时防误触:填错成 50000 的人损失的是 50000 次计数,而真要一次加几千的场景极少。把每步改成 999 加六次即可,日志会分别记录,导出后合计不变。
时间显示的是我的时区吗?
表格里显示 UTC 时间(ISO 8601 串的时分秒部分),和北京时间差 8 小时,比如 UTC 01:30 是北京 09:30。CSV 里保留完整带 Z 的 ISO 串,导入表格软件后可自行转时区。用 UTC 而不是本地时间是故意的:导出的文件发给别人核对时,不存在「你那边的 9 点是我这边的几点」的问题。
和点击速度测试那个页有什么区别?
口径完全不同:这里回答「总共点了几次」,累计、可撤销、刷新不丢;点击速度测试每秒点击回答「每秒能点几次」,按计时窗口内的事件频率算 CPS,测完即弃、不持久化。一个是清点库存,一个是测手速,别混用。
换手机或换浏览器后记录能迁过去吗?
整份记录不能自动迁移(数据只在本机),但可以把 CSV 发到新设备。日志导入功能本页没有做:导入意味着要处理 CSV 解析、时间戳校验、与已有日志合并冲突三件事,出错概率比重新计数还高,不如把「导出即留档」做扎实。
相关工具
- 在线计分板:两队或多队各自记分并排名,比单计数器多一层「谁对谁」的维度。
- 点击速度测试每秒点击:固定时间内测 CPS 手速,测的是频率不是累计。
- 习惯记录:按天打卡看连续天数,适合「每天做没做」而不是「今天做了几次」。
- 在线秒表:计时长不计次数,与计次互补。
- Tap速度BPM查询:按节奏敲击换算 BPM,音乐练拍用。
- 按键键码查询:做自动计次脚本前先查键位对应的 event.code。
- 清单生成:把要清点的项目先列成清单,配合本页逐项计数。