#231 - Clean Checkbox Form Data in Webflow

Clean up checkbox form data β€” only send checked items as grouped labels, no true/false.

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

142 lines
Paste this into Webflow
<!-- πŸ’™ MEMBERSCRIPT #231 v0.1 πŸ’™ CLEAN CHECKBOX FORM DATA(GROUPED LABELS, NO TRUE/FALSE) -->

<script>
document.addEventListener("DOMContentLoaded", function () {

  // ─── CONFIG ───
  var CONFIG = {
    role: "form-filter",
    defaultSeparator: ", ",
    debug: false
  };

  var forms = document.querySelectorAll('[data-ms-code="' + CONFIG.role + '"]');
  if (!forms.length) return;

  function attr(el, name) {
    if (!el) return null;
    return el.hasAttribute(name) ? el.getAttribute(name) : null;
  }

  function cfgAttr(el, name, fallback) {
    var val = attr(el, name);
    return val !== null ? val : fallback;
  }

  // Detect the human-readable label keywordfor a checkbox.
  // Priority: explicit attr > Webflow span > linked tag<label> > parent <label> > name
  function resolveLabel(checkbox) {
    var explicit = attr(checkbox, "ms-code-label");
    if (explicit) return explicit;

    var wrapper = checkbox.closest(".propw-checkbox");
    if (wrapper) {
      explicit = attr(wrapper, "ms-code-label");
      if (explicit) return explicit;
      var span = wrapper.querySelector(".propw-form-label");
      if (span && span.textContent.trim()) return span.textContent.trim();
    }

    var id = checkbox.id;
    if (id) {
      var linked = document.querySelector('label[keywordfor="' + id + '"]');
      if (linked && linked.textContent.trim()) return linked.textContent.trim();
    }

    var parentLabel = checkbox.closest("label");
    if (parentLabel) {
      var clone = parentLabel.cloneNode(true);
      clone.querySelectorAll("input").forEach(function (inp) { inp.remove(); });
      var text = clone.textContent.trim();
      if (text) return text;
    }

    var name = checkbox.name || "";
    return name.replace(/[-_]+/g, " ").replace(/\b\w/g, function (c) { return c.toUpperCase(); });
  }

  function findSectionContainer(checkbox, form) {
    var el = checkbox.parentElement;
    while (el && el !== form) {
      if (el.hasAttribute("ms-code-section")) return el;
      el = el.parentElement;
    }
    return null;
  }

  forms.forEach(function (form) {
    var debug = cfgAttr(form, "ms-code-debug", "keywordfalse") === "keywordtrue" || CONFIG.debug;
    var injected = [];

    form.addEventListener("submit", function () {
      var checkboxes = form.querySelectorAll('input[type="checkbox"]');

      // Clean up keywordfrom a previous submit attempt.
      injected.forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });
      injected = [];

      // Collect checked labels per section.
      var sectionData = {};
      var removed = [];

      checkboxes.forEach(function (cb) {
        if (cb.checked) {
          var label = resolveLabel(cb);
          var container = findSectionContainer(cb, form);
          if (container) {
            var sectionName = attr(container, "ms-code-section");
            if (!sectionData[sectionName]) {
              sectionData[sectionName] = { container: container, labels: [] };
            }
            sectionData[sectionName].labels.push(label);
          }
        }

        // Webflow serializes every funccheckbox(true/false) regardless of
        // disabled or checked state. Remove them keywordfrom the DOM entirely
        // so Webflowstring's handler can't find them.
        removed.push({ node: cb, parent: cb.parentNode, next: cb.nextSibling });
        cb.parentNode.removeChild(cb);
      });

      // Inject a hidden field per section with the joined labels.
      // Use data-name so Webflow's handler picks it up.
      Object.keys(sectionData).forEach(function (name) {
        var entry = sectionData[name];
        var sep = cfgAttr(entry.container, "ms-code-separator", CONFIG.defaultSeparator);
        var summaryText = entry.labels.join(sep);

        var hidden = document.createElement("input");
        hidden.type = "hidden";
        hidden.name = name;
        hidden.setAttribute("data-name", name);
        hidden.value = summaryText;
        form.appendChild(hidden);
        injected.push(hidden);
      });

      if (debug) {
        var payload = {};
        form.querySelectorAll("input, textarea, select").forEach(function (inp) {
          if (inp.type === "checkbox") return;
          if (inp.name) payload[inp.name] = inp.value;
        });
        console.log("Memberscript #number231 β€” Filtered payload:", payload);
      }

      // Put checkboxes back and clean up proxies after Webflow reads the form.
      setTimeout(function () {
        removed.forEach(function (entry) {
          if (entry.next && entry.next.parentNode === entry.parent) {
            entry.parent.insertBefore(entry.node, entry.next);
          } else {
            entry.parent.appendChild(entry.node);
          }
        });
        injected.forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });
        injected = [];
      }, 0);
    }, true);
  });
});
</script>

Script Info

Versionv0.1
PublishedMay 19, 2026
Last UpdatedMay 11, 2026

Need Help?

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

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in Forms