Hey John,
To combine Cornerstone Forms (CS Forms) with Off Canvas or Dropdown functionality while keeping your filter controls linked to the Looper, you need to understand the underlying DOM behavior of these components.
Here is the breakdown of why this happens and how to structure it.
1. The Core Issue: DOM Teleportation vs. Inline Rendering
When combining CS Forms with toggleable elements, the primary issue is DOM placement:
-
Off Canvas / Modals (Teleported): Cornerstone’s Off Canvas and Modal elements automatically detach their content wrapper from where they are placed in the builder and append/teleport it to the end of the
<body> element. This prevents overflow-clipping and z-index issues.
- Because of this, any inputs (like your Checkbox Lists) placed inside the Off Canvas Content Wrapper are physically moved outside the
<form> container in the DOM.
- This breaks two core behaviors in [cornerstone-forms.js](file:///d:/laragon/www/pro/wp-content/plugins/cornerstone-forms/assets/js/cornerstone-forms.js):
-
Serialization: The plugin serializes the form data using
new FormData(formElement). Since the checkboxes are no longer inside the <form>, the browser does not serialize them, and they are omitted from the AJAX payload.
-
Auto-Submit Event Bubbling: The plugin’s AJAX auto-submit script listens for a
change event bubbling up to the form element. Because the checkboxes are in the footer, their change events bubble up to the <body> instead of the form, preventing auto-submit.
-
Dropdowns (Inline): In contrast, Cornerstone Dropdown elements stay inline in the DOM under their relative
.x-mod-container wrapper. They are not teleported to the body. Inputs inside a Dropdown will naturally remain nested inside the <form> element in the DOM tree, requiring no extra linking.
2. Structural Guidelines
Depending on which component you use, follow these rules:
If using a Dropdown element:
-
Off Canvas vs. Dropdown: Dropdowns are much easier to implement because they stay inline.
-
Structure: You can safely put the Dropdown element directly inside the CS Form container. The checkboxes inside the dropdown content will stay inside the
<form> and work natively out-of-the-box.
If using an Off Canvas element:
-
Option A: Form inside Off Canvas (Simple but Self-Contained)
If all filter inputs (including search) can reside inside the Off Canvas Content Wrapper:
-
Structure: Place the Form Container directly inside the Off Canvas Content Wrapper, wrapping all your filters/inputs.
-
Result: Since the Form itself is teleported with the content wrapper, all inputs remain inside the
<form> tag in the DOM, maintaining full functionality.
-
Option B: Off Canvas inside Form (For Search Field Outside, Filters Inside)
If you want the Search field to be outside the Off Canvas (e.g. on the main page) and checkboxes/filters inside the Off Canvas:
-
Structure: Place the Form Container on the outer layout level, wrapping both the Search field and the Off Canvas element.
-
Result: The Search field remains inside the Form in the DOM, but the checkboxes in the Off Canvas Content Wrapper teleport to the body (becoming unlinked). You must bridge them manually using the method below.
3. Solution for Option B (Search Outside, Filters Inside)
To keep the teleported Off Canvas inputs linked to the outer Form container, implement these two steps:
Step 1: Link inputs via the HTML form attribute
In the Cornerstone builder, inspect the inputs (such as the Checkbox List) inside the Off Canvas and add a custom attribute:
-
Attribute:
form
-
Value:
[ID of your outer CS Form element] (e.g., my-filter-form)
Why this helps: Standard HTML5 and the JS FormData API will now associate these inputs with the form ID, causing them to be successfully serialized and submitted even when physically outside the form.
Step 2: Bridge change events via Custom JS
Since the inputs are outside the form, their change events will not bubble up to the form to trigger the AJAX auto-submit. Add the following JavaScript to your site (e.g., in Pro > Custom JS or via a child theme) to bridge the events:
Please just note that the following code is theoretical. This serves only as a guide. We cannot provide support for it. For that, you can subscribe to our One service.
document.addEventListener('change', function(e) {
if (e.target && e.target.getAttribute('form')) {
const formId = e.target.getAttribute('form');
const form = document.getElementById(formId);
if (form) {
// Manually trigger the form's change handler so it auto-submits via AJAX
form.dispatchEvent(new Event('change', { bubbles: true }));
}
}
});
This globally listens to changes on any input with a form attribute and forwards the event to its target form, keeping the CS Form Looper filtering active.