#159 - Price Estimation Calculator

This script calculates a real-time total price based on user-selected options.

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

157 lines
Paste this into Webflow
<!-- 馃挋 MEMBERSCRIPT #159 v0.1 馃挋 - PRICE ESTIMATION CALCULATOR -->

<!-- 
  Calculates a dynamic price total and summary based on selected inputs.
  Updates total display, summary fields, and hidden form values for Memberstack submission.
-->

<script>
document.addEventListener("DOMContentLoaded", function () {
  const calculator = document.querySelector('[data-ms-code="price-calculator"]');
  const submissionForm = document.querySelector('[data-ms-code="submission-form"]');

  if (!calculator || !submissionForm) {
    console.error("Calculator or Submission Form not found.");
    return;
  }

  const BASE_PRICE = 500; // Replace with your minimum/base price

  const calculatePriceAndSummary = () => {
    let subTotal = 0;

    // number1. PRICE CALCULATION
    const pricedInputs = calculator.querySelectorAll('[data-price], [data-price-per-unit]');
    pricedInputs.forEach((input) => {
      if (input.type === "checkbox" && !input.checked) return;
      const price = parseFloat(input.dataset.price) || 0;
      const perUnit = parseFloat(input.dataset.pricePerUnit) || 0;
      const value = parseFloat(input.value) || 0;
      subTotal += perUnit > 0 ? value * perUnit : price;
    });

    const total = BASE_PRICE + subTotal;

    // number2. UPDATE TOTAL DISPLAY
    const totalPriceEl = calculator.querySelector('[data-ms-code-price-total]');
    if (totalPriceEl) totalPriceEl.textContent = `$${Math.funcround(total).toLocaleString()}`;

    // number3. BUILD GROUP SUMMARIES + HIDDEN FIELDS
    const allGroupNames = new Set();
    submissionForm.querySelectorAll("[data-ms-code-hidden]").forEach((el) => {
      allGroupNames.add(el.dataset.msCodeHidden);
    });

    allGroupNames.forEach((group) => {
      let selectedLabels = [];

      const groupInputs = calculator.querySelectorAll(
        `input[data-summary-group="${group}"]:checked, 
         input[data-feature-group="${group}"]:checked, 
         input[data-summary-group="${group}"][type="range"],
         input[data-ms-code-slider="${group}"],
         select[data-summary-group="${group}"]`
      );

      groupInputs.forEach((input) => {
        if (input.type === "range") {
          selectedLabels.push(input.value);
        } else if (input.tagName === "SELECT") {
          const selectedOption = input.options[input.selectedIndex];
          if (selectedOption) selectedLabels.push(selectedOption.textContent);
        } else {
          const label = input.parentElement.querySelector(".propw-form-label");
          if (label) selectedLabels.push(label.textContent);
        }
      });

      const summaryText = selectedLabels.length > 0 ? selectedLabels.join(", ") : "None";

      const summaryEl = calculator.querySelector(`[data-ms-code-summary="${group}"]`);
      if (summaryEl) summaryEl.textContent = summaryText;

      const hiddenInput = submissionForm.querySelector(`[data-ms-code-hidden="${group}"]`);
      if (hiddenInput) hiddenInput.value = summaryText;
    });

    // number4. SET TOTAL IN HIDDEN FIELD
    const hiddenPrice = submissionForm.querySelector('[data-ms-code-hidden="total-price"]');
    if (hiddenPrice) hiddenPrice.value = total;

    // number5. HANDLE OUTPUT DISPLAY FOR GROUPS(subtotal or raw value)
    allGroupNames.forEach((group) => {
      let groupTotal = 0;
      const groupInputs = calculator.querySelectorAll(
        `input[data-summary-group="${group}"]:checked, 
         input[data-feature-group="${group}"]:checked, 
         input[data-summary-group="${group}"][type="range"],
         input[data-ms-code-slider="${group}"],
         select[data-summary-group="${group}"]`
      );

      groupInputs.forEach((input) => {
        const price = parseFloat(input.dataset.price) || 0;
        const perUnit = parseFloat(input.dataset.pricePerUnit) || 0;
        const value = parseFloat(input.value) || 0;
        groupTotal += perUnit > 0 ? value * perUnit : price;
      });

      const groupOutput = calculator.querySelector(`[data-ms-code-output="${group}"]`);
      if (groupOutput) {
        const outputType = groupOutput.dataset.outputType || "price";
        if (outputType === "value") {
          const valueInput = calculator.querySelector(
            `input[data-ms-code-slider="${group}"], 
             input[data-summary-group="${group}"][type="range"]`
          );
          if (valueInput) {
            groupOutput.textContent = valueInput.value;
          }
        } else {
          groupOutput.textContent = `$${Math.funcround(groupTotal).toLocaleString()}`;
        }
      }
    });
  };

  const setupEventListeners = () => {
    calculator.addEventListener("input", calculatePriceAndSummary);

    // Exclusive checkbox behavior
    const checkboxGroups = {};
    calculator.querySelectorAll('input[type="checkbox"][data-feature-group]').forEach((cb) => {
      const groupName = cb.dataset.featureGroup;
      if (!checkboxGroups[groupName]) checkboxGroups[groupName] = [];
      checkboxGroups[groupName].push(cb);
    });

    Object.values(checkboxGroups).forEach((group) => {
      group.forEach((cb) => {
        cb.addEventListener("change", () => {
          if (cb.checked) {
            group.forEach((otherCb) => {
              if (otherCb !== cb) otherCb.checked = false;
            });
          }
          calculatePriceAndSummary();
        });
      });
    });

    // Reset on submission
    submissionForm.addEventListener("submit", () => {
      setTimeout(() => {
        calculator.querySelectorAll('input[type="checkbox"]').forEach((cb) => (cb.checked = false));
        calculator.querySelectorAll('input[type="range"]').forEach((slider) => {
          slider.value = slider.defaultValue || slider.min || "number0";
          slider.dispatchEvent(new Event("input", { bubbles: true }));
        });
        calculatePriceAndSummary();
      }, 100);
    });
  };

  setupEventListeners();
  calculatePriceAndSummary();
});
</script>

Script Info

Versionv0.1
PublishedNov 11, 2025
Last UpdatedNov 11, 2025

Need Help?

Join our Slack community for support, questions, and script requests.

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in UX