如何用JavaScript检测连续复选框选中并实现折扣计算
问题需求与代码优化方案
需求说明
有7个带有data-calculate属性的复选框表单,需实现:
- 当存在3-5个连续选中的复选框时,对该连续区间内的复选框应用第一档折扣(修改
data-calculate值为84.15) - 当存在5个及以上连续选中的复选框时,对该连续区间内的复选框应用第二档折扣(示例值为
79.2,可自行调整) - 原代码在取消勾选时,处理数组项移除和折扣逻辑存在错误,导致功能失效
原问题代码
JavaScript代码
let _log = (item) => { return console.log(item) } const week1 = document.querySelector(".c_week_1"); const week2 = document.querySelector(".c_week_2"); const week3 = document.querySelector(".c_week_3"); const week4 = document.querySelector(".c_week_4"); const week5 = document.querySelector(".c_week_5"); const week6 = document.querySelector(".c_week_6"); const week7 = document.querySelector(".c_week_7"); // Getting data attribute value let week1Calc = week1.dataset.calculate; let week2Calc = week2.dataset.calculate; let week3Calc = week3.dataset.calculate; let week4Calc = week4.dataset.calculate; let week5Calc = week5.dataset.calculate; let week6Calc = week6.dataset.calculate; let week7Calc = week7.dataset.calculate; // Output price let price = document.querySelector(".c_final_price_1_kid"); // Initalial num let count = 0; // Arrays // Selecting all inputs let allCheckboxes = [week1, week2, week3, week4, week5, week6, week7]; // Selecting all inputs data-calculate value let allCheckboxesCalcValues = [week1Calc, week2Calc, week3Calc, week4Calc, week5Calc, week6Calc, week7Calc]; // Adding all inputs that are checked let checkboxesCheckedArray = []; // Getting checkboxesCheckedArray index numbers let checkboxesCheckedArrayNumbers = []; // Adding the checked checkbox data-calculate attribute in the array let checkboxesCalculateValuesArray = []; // Selecting all checkboxes and their index allCheckboxes.forEach((box, index) => { box.addEventListener("change", (e) => { if (box.checked) { // Check how many checkboxes are checked count++; // Adding checked checkboxes to the array checkboxesCheckedArray.push(box); // Adding checked index to the array checkboxesCheckedArrayNumbers.push(index); // Sorting numbers array checkboxesCheckedArrayNumbers.sort(); checkboxesCalculateValuesArray.push(e.target.dataset.calculate); price.textContent = checkboxesCalculateValuesArray.reduce((accumulator, currentValue) => { return parseInt(accumulator) + parseInt(currentValue); }); for (let i = 1; i < checkboxesCheckedArrayNumbers.length - 1; i++) { // Check if 3 or more are in consecutive order if (checkboxesCheckedArrayNumbers[i - 1] === checkboxesCheckedArrayNumbers[i] - 1 && checkboxesCheckedArrayNumbers[i + 1] === checkboxesCheckedArrayNumbers[i] + 1) { // When there are 3 up to 5 consecutive weeks then loop here to change data-calculate values to 84.15 price.textContent = checkboxesCalculateValuesArray.reduce((accumulator, currentValue) => { return parseInt(accumulator) + parseInt(currentValue); }); _log(checkboxesCalculateValuesArray) } // Check if 5 or more are in consecutive order if (checkboxesCheckedArrayNumbers[i - 2] === checkboxesCheckedArrayNumbers[i] - 2 && checkboxesCheckedArrayNumbers[i + 2] === checkboxesCheckedArrayNumbers[i] + 2) { } } } else { count--; console.log(count); // Adding checked checkboxes to the array checkboxesCheckedArray.pop(box); // Adding checked index to the array checkboxesCheckedArrayNumbers.pop(index); // Sorting numbers array checkboxesCheckedArrayNumbers.sort(); checkboxesCalculateValuesArray.pop(e.target.dataset.calculate); for (let i = 1; i < checkboxesCheckedArrayNumbers.length - 1; i++) { // Check if 3 or more are in consecutive order if (checkboxesCheckedArrayNumbers[i - 1] === checkboxesCheckedArrayNumbers[i] - 1 && checkboxesCheckedArrayNumbers[i + 1] === checkboxesCheckedArrayNumbers[i] + 1) { } // Check if 5 or more are in consecutive order if (checkboxesCheckedArrayNumbers[i - 2] === checkboxesCheckedArrayNumbers[i] - 2 && checkboxesCheckedArrayNumbers[i + 2] === checkboxesCheckedArrayNumbers[i] + 2) { } } } }) }) let customForm = document.getElementsByTagName("form")[0]; const submitButton = document.querySelector(".c-main-calc-submit--button"); customForm.addEventListener("submit", (e) => { customForm.submit(); })
HTML代码
<form id="form"> <div class="c-checkbox-box"> <span>1</span> <input type="checkbox" class="c_week_1" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>2</span> <input type="checkbox" class="c_week_2" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>3</span> <input type="checkbox" class="c_week_3" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>4</span> <input type="checkbox" class="c_week_4" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>5</span> <input type="checkbox" class="c_week_5" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>6</span> <input type="checkbox" class="c_week_6" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>7</span> <input type="checkbox" class="c_week_7" data-calculate="99"> </div> </form> <div class="c_final_price_1_kid"></div>
优化后的解决方案
核心改进点
- 移除冗余的单独变量,统一用数组管理所有复选框
- 取消勾选时不再单独处理数组移除,改为每次状态变化后重新扫描所有选中项,避免数组同步错误
- 新增函数识别连续选中的区间,根据区间长度自动应用对应折扣
- 统一更新
data-calculate值和总价,确保逻辑一致性
优化后代码
JavaScript代码
// 配置参数:可根据需求调整折扣值 const PRICE_ORIGINAL = 99; const PRICE_DISCOUNT_1 = 84.15; // 3-5个连续选中 const PRICE_DISCOUNT_2 = 79.2; // 5个及以上连续选中 // 获取DOM元素 const allCheckboxes = Array.from(document.querySelectorAll('input[type="checkbox"]')); const priceDisplay = document.querySelector(".c_final_price_1_kid"); const customForm = document.getElementById("form"); // 找出所有连续选中的区间 function getConsecutiveRanges(checkedIndices) { if (checkedIndices.length === 0) return []; const ranges = []; let currentStart = checkedIndices[0]; let currentEnd = checkedIndices[0]; for (let i = 1; i < checkedIndices.length; i++) { if (checkedIndices[i] === currentEnd + 1) { currentEnd = checkedIndices[i]; } else { ranges.push({ start: currentStart, end: currentEnd }); currentStart = checkedIndices[i]; currentEnd = checkedIndices[i]; } } ranges.push({ start: currentStart, end: currentEnd }); return ranges; } // 计算总价并更新data-calculate属性 function updatePriceAndDiscounts() { // 获取所有选中复选框的索引并排序 const checkedIndices = allCheckboxes .map((box, idx) => box.checked ? idx : null) .filter(idx => idx !== null) .sort((a, b) => a - b); // 重置所有复选框的原始价格 allCheckboxes.forEach(box => { box.dataset.calculate = PRICE_ORIGINAL.toString(); }); // 处理连续区间的折扣 const consecutiveRanges = getConsecutiveRanges(checkedIndices); consecutiveRanges.forEach(range => { const rangeLength = range.end - range.start + 1; let discountPrice = PRICE_ORIGINAL; if (rangeLength >= 5) { discountPrice = PRICE_DISCOUNT_2; } else if (rangeLength >=3 && rangeLength <=5) { discountPrice = PRICE_DISCOUNT_1; } // 更新区间内所有复选框的data-calculate值 for (let i = range.start; i <= range.end; i++) { allCheckboxes[i].dataset.calculate = discountPrice.toString(); } }); // 计算总价并显示 const total = allCheckboxes .filter(box => box.checked) .reduce((sum, box) => sum + parseFloat(box.dataset.calculate), 0); priceDisplay.textContent = total.toFixed(2); // 保留两位小数 } // 给每个复选框绑定change事件 allCheckboxes.forEach(box => { box.addEventListener("change", updatePriceAndDiscounts); }); // 表单提交事件(保留原逻辑) customForm.addEventListener("submit", (e) => { e.preventDefault(); // 防止默认提交,可根据需求移除 customForm.submit(); }); // 初始化计算 updatePriceAndDiscounts();
HTML代码(复用原代码即可)
<form id="form"> <div class="c-checkbox-box"> <span>1</span> <input type="checkbox" class="c_week_1" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>2</span> <input type="checkbox" class="c_week_2" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>3</span> <input type="checkbox" class="c_week_3" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>4</span> <input type="checkbox" class="c_week_4" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>5</span> <input type="checkbox" class="c_week_5" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>6</span> <input type="checkbox" class="c_week_6" data-calculate="99"> </div> <div class="c-checkbox-box"> <span>7</span> <input type="checkbox" class="c_week_7" data-calculate="99"> </div> </form> <div class="c_final_price_1_kid"></div>
关键逻辑解释
- 连续区间识别:
getConsecutiveRanges函数遍历选中的索引,将连续的索引归为一个区间,比如选中索引0、1、2会被识别为{start:0, end:2}的区间。 - 折扣应用:每次状态变化时,先重置所有复选框的原始价格,再根据每个连续区间的长度应用对应折扣,确保折扣只作用于连续选中的项。
- 总价计算:基于更新后的
data-calculate值计算总价,保证显示的价格与实际折扣一致。 - 取消勾选处理:无需单独处理移除逻辑,每次变化后重新扫描所有选中项,彻底避免原代码中数组同步错误的问题。
内容的提问来源于stack exchange,提问作者NoName84
相关产品推荐
相关产品推荐

