Guide 10 · Developer docs

Forms and data capture

Building forms with the Forms module: how a form is put together and placed on a page, validating and branching, presenting it, where submissions are stored, and what happens after one arrives, from jobs and records to CRMs and payments.

L1–L2
Unit 07 · Reference

js-forms.js reference

The platform's script for the JS model: the markup it expects, the five callbacks and what each receives, the request it sends, what it does with the answer, and how to show an error answer so the visitor sees it and keeps what they typed.

Who this is for. The front-end developer building a form layout on the JS model, or debugging one. Multi-step forms explains the model and when to choose it; this page is the contract.

The file

Served by every site at /assets/forms/js-forms.js, with its stylesheet at /assets/forms/js-forms.css, which defines the js-hidden class that hides the steps not shown. It is the platform's, updated with the platform; do not copy it into the theme. Load it on the pages that carry a JS-model form, with the page's nonce (the snippet).

It starts itself once. When the page has loaded (or at once, if it is loaded later), it sets up every .coredna-js-form already in the page. Render the form with the page; the script sets up every form present when it loads.

The markup it expects

ElementRole
.coredna-js-formThe container, one per form. The callbacks and data-endpoint are attributes on it
input[name="_formId"]Inside the container, holding the form id. Required: without it the script logs "_formId input is required" and leaves the form alone
.form-stepOne per step, in order. The script numbers them from 1 into data-step. The last .form-step is the result step, shown after a successful submit
.form-step formOne per step but the result step. Submitting a step's form moves to the next step; submitting the last form sends the whole form
.navigate-previousA control that goes back one step
.go-to-step with data-step="n"A control that goes to step n, counted from 1
data-skipped="true" on a stepNavigation passes over it. js-forms-conditionals.js sets it for a step hidden by a rule
.submit-error-messageInside a step: shown when a submit fails. %message% in it is replaced by the error's message; otherwise its content is replaced
input[name="_redirectUrl"]Optional. After a successful submit the browser goes to its value instead of showing the result step

Because each step is a real <form>, the browser checks required, pattern and the input types before the script sees the submit, so a step with an empty required field does not advance.

Attributes on the container

AttributeValue
data-endpointWhere to send the form. The default is /cdna-api/forms/submission/FORM_ID; FORM_ID in the value is replaced by the form id
data-start-callbackThe name of a function on window, below
data-navigate-callback
data-submit-modifier-callback
data-submit-callback
data-submit-result-callback

The callbacks

Each attribute names a global function, window[name]; a name that is not a function is ignored. The start callback is looked up once, when the script sets the form up, so it must exist by then. The others are looked up each time they are needed, so a script loaded later can set them; js-payment-element.js does. Every callback receives one object with formContainer, formId, currentStep and currentStepNumber, plus:

CallbackWhenAlso receivesReturn
data-start-callbackThe first keystroke or change in any fieldeventIgnored
data-navigate-callbackBefore moving to another stepdata (every answer so far), requestedStep, requestedStepNumberfalse, or a promise of false, stays on the step
data-submit-modifier-callbackWhen the last form is submitted, before anything is sentdata, resultStep, resultStepNumber, eventThe data to send, or a promise of it. Throwing shows the error's message in .submit-error-message
data-submit-callbackAfter the modifier, before sendingThe same, with the modified datafalse, or a promise of false, cancels
data-submit-result-callbackWhen the answer arrivesdata, resultStep, resultStepNumber, xhrTruthy lets the script handle the answer; falsy means you have

A cancelled submit leaves the buttons disabled. The script disables the last step's submit buttons as soon as it is submitted, and only an error it handles itself enables them again. A modifier that returns nothing, a submit callback that returns false and a submit callback that throws all stop the submit with the buttons still disabled: enable them again in your callback before stopping.

The request it sends

One POST with a JSON body: every field of every step's form, merged into one object by name, so a name used on two steps keeps the later step's value. Fields are read like this:

FieldSent as
Text, select, textarea and the other inputsThe value
A checkbox alone under its nameIts value when ticked; left out when not
Several checkboxes with one nameA list of the ticked values
Radio buttonsThe chosen value, or ""
A multiple selectA list
A file inputThe file as a data URL (data:…;base64,…), or null when none is chosen. A name ending in [] sends an object of data URLs keyed "0", "1"…
Buttons, submit and image inputs, fields without a name or idNot sent

Hidden fields go too, so _formId and any Hidden elements filled by your helpers are part of the body. The submission route takes the body as it is; files sent this way are stored without their names.

A double submit is stopped. While a submit is in progress, a second submit event on the last form is ignored, until the answer arrives.

What it does with the answer

  • A 2xx other than 207: goes to _redirectUrl if the container has one (replacing the page in the history), or shows the result step.
  • Anything else, a 207, a 422, a 500, needs data-submit-result-callback to show the visitor anything (below).

A 202 for a pending payment counts as a success here, and the result step would be shown before the payment is made. On a form with a payment element, js-payment-element.js takes the answer over first.

Handling an error answer

On a 207, a 422 or a 500, handle the response yourself: in data-submit-result-callback, return false, which every JS-model form needs, build each field's message as a new element rather than rewriting the field's wrapper with innerHTML += (which would restore every field in it to its original markup), and re-enable the submit buttons. The callback is on Validation.

Go deeper

Was this page helpful?

Your answer goes to the docs team.