You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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>

优化后的解决方案

核心改进点

  1. 移除冗余的单独变量,统一用数组管理所有复选框
  2. 取消勾选时不再单独处理数组移除,改为每次状态变化后重新扫描所有选中项,避免数组同步错误
  3. 新增函数识别连续选中的区间,根据区间长度自动应用对应折扣
  4. 统一更新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>

关键逻辑解释

  1. 连续区间识别:getConsecutiveRanges函数遍历选中的索引,将连续的索引归为一个区间,比如选中索引0、1、2会被识别为{start:0, end:2}的区间。
  2. 折扣应用:每次状态变化时,先重置所有复选框的原始价格,再根据每个连续区间的长度应用对应折扣,确保折扣只作用于连续选中的项。
  3. 总价计算:基于更新后的data-calculate值计算总价,保证显示的价格与实际折扣一致。
  4. 取消勾选处理:无需单独处理移除逻辑,每次变化后重新扫描所有选中项,彻底避免原代码中数组同步错误的问题。

内容的提问来源于stack exchange,提问作者NoName84

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 03:22:08