利用HTML表格制作抽奖界面需使用原生table+tr+td结构,确保DOM层级与二维坐标对齐。通过data-index或data-row+col属性标识格子,避免依赖文本或class。抽奖时立即禁用所有td并添加遮罩层防止多次点击。中奖高亮区分当前和历次记录,使用class管理状态。
抽奖界面这事儿,看着简单,但真要做得可靠、可维护,里头有不少坑。特别是用HTML表格来做,很多人第一反应是用CSS Grid或者Flexbox糊一个网格,觉得“看起来像表格就行”。实际上,真到了需要精确控制每个格子的时候,比如随机选中、高亮、禁用,DOM结构和逻辑索引必须严格对齐,否则后面全是麻烦。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
table + tr + td,不能靠 CSS Grid 伪装抽奖界面的核心问题在于,视觉上的网格和逻辑上的行列索引必须对齐。光看表面像格子可不够。用 div 加 display: grid 拼出来的“表格”,Ja vaScript 没法通过 DOM 精确到第几行第几列。后续要做随机选中、高亮、禁用,定位会乱套。
可靠的做法是:直接用原生 table,每行是 tr,每个奖品格子是 td。这样 DOM 层级天然就反映了二维坐标,rowIndex 和 cellIndex 可以直接读取,省心又精确。
td 里嵌套 div 再塞内容——除非只是做装饰,否则会干扰点击区域和事件绑定。colspan 或 rowspan 合并单元格。抽奖格子必须是一一对应的独立 td,否则 Math.floor(Math.random() * totalCells) 算出的索引可能会落空或重复。tr,每个含 4 个 td。动态生成也行,但最终 DOM 必须是一个规整的矩形,不能有缺块。td 绑定唯一标识,别依赖 innerText 或 class 名抽奖时,得明确知道“抽中了哪个格子”。靠 td.innerText === "一等奖" 去匹配?那相当不可靠——文本可能重复、带空格、被翻译,或者后期改文案。class 名如 prize-1 也不保险,容易误删或冲突。
更稳妥的方式:用自定义属性 data-index 或 data-row + data-col,初始化时就写死。比如:
谢谢参与 再来一次
data-index 适合一维遍历,用 table.querySelectorAll('td') 后直接按索引取。data-row 和 data-col 适合二维逻辑,比如禁止相邻格子连续中奖,或者高亮整行。id。大量 td 会导致 ID 重复的风险,而且 Ja vaScript 查询 data- 属性比 id 更快更稳定。td 并加 loading 状态用户手速快、网络延迟、Ja vaScript 执行慢,都可能导致多次点击,结果抽中多个奖。最简单的办法不是靠防抖函数,而是从 DOM 层面即时锁定。
td,设置 pointer-events: none,并添加一个 class 如 disabled。注意,td 本身不支持 disabled 属性,所以得用 CSS 控制。div 覆盖),从视觉上阻止用户再点击。td 的样式,包括背景色、边框、透明度。否则下次抽奖时,视觉状态会错乱。一个常见错误是:只禁用按钮,不锁 td;或者用 setTimeout 模拟抽奖动画,却没清定时器,导致多次回调叠加。
如果只是单次抽奖,高亮一个格子就够了。但很多需求要保留历史记录,比如显示“已抽中:3 个二等奖”。这时,不能只改 background-color,否则颜色叠加起来根本分不清。
td.current-win 负责动效和边框闪烁,td.history-win 负责浅灰底和小图标。逻辑清晰,样式可复用。backgroundColor。这不利于主题切换,也不利于 CSS 覆盖。data- 属性传过去,而不是从 DOM 里重新查询。因为异步操作时,节点可能已被移除或替换。真正麻烦的是多轮抽奖后,某个格子状态混合:它既是本轮中奖,又是历史中过奖。这时候,class 列表管理比单纯 classList.toggle 一刀切要稳妥得多。别偷懒。