Files
form-validation/README.md
Dan Remollino 2cc6b61b33 the target of 'dependent' must now contain a value that passes
validation in order to remove the disabled status of the dependent field
2025-04-12 22:48:25 -04:00

18 KiB

Demo

A feature demo can be found here.

Documentation

Importing the Validation class queries the current page for <form> elements and disables the default browser constraint validation by adding a novalidate attribute to that element.

The default behavior is to validate each field when their value is updated, and validate the entire form when it is submitted. This can be changed to only validate on form submission by setting onlyOnSubmit to true on the instance.

Once a field is marked invalid, it will always re-validate when its value is changed regardless of the onlyOnSubmit value.

required fields are first tested for blank. If this check is passed, fields are then tested based on their type attribute value if applicable. (Described below.) Lastly, the field will test for a user supplied pattern if present.

Required Fields

Usually, the majority of fields in a form need validation. To make setup easier, instances of the Validation class have a default reqAll value of true. This marks all fields that meet the following criteria required and needing validation:

Indivudual fields can then be excluded from validation as described below.

This functionality can be inverted by setting reqAll to false, and manually giving your required fields a required attribute.

<div class="form-group">
    <label for="text">Text</label>
    <input required id="text" type="text" name="text">
</div>

<input> type attribute values of checkbox and radio only need one option to have the required attribute, however, it is recommended to add it to all options for consistency. More information on checkbox and radio fields can be found below.

Required Indicator

A <span> tag with class of required__indicator containing an asterisk(*) character will be inserted beforeend of a required field's <label> element.

This indicator will be inserted to the <legend> element of checkbox and radio <input> types if present as described below.

The indicator can be disabled or customized as described in Form Options.

Markup

The Validation class requires the following for each required field:

  • All form fields to be wrapped in an element with the form-group class.
  • Do NOT nest the required field in a <label> element.
  • Unique, matching values of the required field's id and its <label>'s for attribute.
  • A unique name attribute on the required field. checkbox and radio <input> types should share the same unique name attribute.

When a field fails validation the wrapper .form-group element will receive an invalid CSS class. Use this to write your own CSS to indicate an invalid state.

<div class="form-group">
    <label for="text">Text</label>
    <input id="text" type="text" name="text">
</div>

Checkbox and Radio Fields

  • Wrap all options in a <fieldset> element and add the form-group class to it instead of the individual options.
  • The <legend> tag may be used to give the option set a heading.
  • Field Options are shared amongst all inputs in a check or radio group. Your options only need to be applied to one of the inputs in the group that shares a name attribute.
<fieldset class="form-group">
    <legend>Check Group</legend>
    <span class="form-group__item">
        <input data-validation='{"minChecks": 2, "optional": true}' id="checkbox-1" type="checkbox" name="checks">
        <label for="checkbox-1">Checkbox 1</label>
    </span>
    <span class="form-group__item">
        <input id="checkbox-2" type="checkbox" name="checks">
        <label for="checkbox-2">Checkbox 2</label>
    </span>
    <span class="form-group__item">
        <input id="checkbox-3" type="checkbox" name="checks">
        <label for="checkbox-3">Checkbox 3</label>
    </span>
</fieldset>

Select Fields

Required <select> elements should contain an <option> with the selected attribute and an empty value attribute. If not present the element will pass validation even while being required.

<div class="form-group">
    <label for="select">Select</label>
    <select id="select" name="select">
        <option selected value="">Choose an Item</option>
        <option>1</option>
        <option>2</option>
        <option>3</option>
    </select>
</div>

Password Fields

When an <input> element with a type attribute of password is detected on the page, a required Password Confirm field will auto generate after its parent .form-group wrapper.

The markup for this field is a duplicate of the original input's parent .form-group wrapper. The id and name attributes of the <input> element, and the for attribute of the <label> element will have their values set to password-confirm.

This functionality can be disabled by settting passConfirm to false.

A Password field with the markup of

<div class="form-group">
    <label for="password">Password</label>
    <input id="password" type="password" name="password">
</div>

will generate a Password Confirm field with the markup of

<div class="form-group">
    <label for="password-confirm">Password Confirm<span class="required__indicator">*</span></label>
    <input id="password-confirm" type="password" name="password-confirm">
</div>

The Password field is tested that it matches its corresponding Password Confirm field when the Password Confirm field contains a non-blank value.

The Password Confirm field is always tested that it matches its corresponding Password field.

Custom Form Controls

Custom form controls can be validated by informing the ValidationField class what you want to check against as the value for your control. You can determine validity based on:

  1. Existence of an element.
  2. TextContent of an element.
  3. Existence of an attribute.
  4. Value of an attribute.

To validate a custom form control, add a data-validation attribute to an element of the control. The value should be a single quoted JSON object with valueSrc as a property.

The element that the data-validation attribute is added to MUST have a unique id and name attribute. Having an associated label/legend tag with a for attribute is optional, but recommended.

The value of the property should be an array with up to three items.

valueSrc[0]: A CSS selector to target the element to reference for value.
valueSrc[1]: The keyword attribute, attributeValue, exists, or textContent.
valueSrc[2]: If using attribute or attributeValue, the attribute to reference. If using exists, a CSS selector relative to valueSrc[0] as the parent.

valueSrc can be combined with pattern and patternMessage field options to check for specific values and output custom error messages. See the examples below.

Existence of an Element

Determine validity based on whether an element exists or not.

Valid if... the target element exists.

The following example checks for the presence of in image element that is a child of the element with an id of 'custom-control'.

<div class="form-group">
    <label for="custom-control">Custom Form Control</label>
    <div data-validation='{"valueSrc": ["#custom-control", "exists", "img"]}' id="custom-control" name="custom_control"></div>
</div>

The third item in the valueSrc array can be any valid CSS selector string that querySelector can parse. e.x. :scope > img

TextContent of an Element

Use the target element's textContent to determine validity. Useful when using contenteditable.

Valid if... the target element's textContent property is a non-empty string.

The following example checks if the element with an id of 'custom-control' has a non-empty string textContent property.

<div class="form-group">
    <label for="custom-control">Custom Form Control</label>
    <div data-validation='{"valueSrc": ["#custom-control", "textContent"]}' id="custom-control" name="custom_control" contenteditable></div>
</div>

Existence of an Attribute

Use the target element's specified attribute existence to determine validity.

Valid if... the target element's specified attribute exists.

The following example checks if the element with an id of 'custom-control' has a title attribute.

<div class="form-group">
    <label for="custom-control">Custom Form Control</label>
    <div data-validation='{"valueSrc": ["#custom-control", "attribute", "title"]}' id="custom-control" name="custom_control"></div>
</div>

The value of a boolean attribute will evaluate as false and invalid unless it is a non-empty string. When using this option, the attribute must use the checked="checked" (true, valid) and checked="" (false, invalid) form and not checked. checked will evaluate as false and invalid whether present or not.

Value of an Attribute

Use the target element's specified attribute value to determine validity.

Valid if... the value of the target's specified attribute is a non-empty string.

The following example checks if the element with an id of 'custom-control' has a title attribute with a non-empty string value.

<div class="form-group">
    <label for="custom-control">Custom Form Control</label>
    <div data-validation='{"valueSrc": ["#custom-control", "attributeValue", "title"]}' id="custom-control" name="custom_control"></div>
</div>

The value of a boolean attribute will evaluate as false and invalid unless it is a non-empty string. When using this option, the attribute must use the checked="checked" (true, valid) and checked="" (false, invalid) syntax. If you need to check a boolean attribute use attribute.

Form Options

Default instance options can be overwritten by adding a single quoted data-validation attribute on the <form> being validated. Its value must be a properly formatted JSON object.

<form data-validation='{"onlyOnSubmit": true, "reqIndicator": "❗️"}'>
    <div class="form-group">
        <label for="text">Text</label>
        <input id="text" type="text" name="text">
    </div>
    <div class="form-group">
        <button type="submit">Submit</button>
    </div>
</form>

invalidClass

Default: invalid

The class assigned to the parent <div class="form-group" /> wrapper of the invalid field.

messageClass

Default: invalid__message

The class assigned output message of the invalid field.

onlyOnSubmit

Default: false

Only validate the form on submission. A setting of true will disable individual field validation. Regardless of this setting fields will always validate on the valFieldsOn value while they are invalid.

onSuccess

Default: null

Run a script when the form is submited and all fields pass validation. A non truthy value will cancel the form's default action.

<form data-validation='{"onSuccess": "save()"}'>...</form>

passConfirm

Default: true

Automatically create a confirm password field. The markup from the user created field with a type="password" attribute will be duplicated and modifed, then inserted after the original field.

<form data-validation='{"passConfirm": true}'>...</form>

reqAll

Default: true

Automatically add a required attribute to all <input>, <select>, and <textarea> tags in the <form>. This allows you to skip marking individual fields as required in your markup.

Individual fields can be excluded from validation by adding a data-validation attribute to a field that's value is a JSON object with an optional property value set to true.

<div class="form-group">
    <label for="not-required">Not Required</label>
    <input data-validation='{"optional": true}' id="not-required" type="text" name="not_required">
</div>

reqIndicator

Default: *

The string to render after the label text to indicate to the user that the field is required.

reqIndicators

Default: true

Enable the output of reqIndicator strings.

If set to false, you can target all field labels with the CSS selector below to apply a custom style.

.form-group:has([required])
:where(label:has(:not(.form-group__item)), legend) {
    /* Your styles here. */
}

reqIndicatorClass

Default: required__indicator

The class assigned to the <span> wrapper of the outputted reqIndicator.

valFieldsOn

Default: input

The event that triggeers individual field validation. Value can be any JavaScript event, but it is recommended to try blur, change, or input.

Individual field validation can be disable by setting onlyOnSubmit to true.

Field Options

Individual fields can be passed options to customize their behavior by adding a single quoted data-validation attribute to a field. Its value must be a properly formatted JSON object with the properties and values below.

<div class="form-group">
    <label for="custom">Custom Regex</label>
    <input data-validation='{"optional": true,
                             "pattern": "^[0-9]*$",
                             "patternMessage": "This field must only contain numbers."
                            }'
           id="custom" type="text" inputmode="numeric" name="custom">
</div>

dependent

Default: null

Setting the dependent value of one field to another field's name attribute value will disable the field when its dependent field does not contain a valid value.

<figure class="form-group">
    <label for="dependent-parent">Dependent Parent</label>
    <input id="dependent-parent" type="text" name="dependent_parent">
    <figcaption>This field dynamically enables/disables the 'Dependent Child' field if it has a value.</figcaption>
</figure>

<figure class="form-group">
    <label for="dependent-child">Dependent Child</label>
    <input data-validation='{"dependent": "dependent_parent"}' id="dependent-child" type="text" name="dependent_child">
    <figcaption>This field dynamically enables/disables if the 'Dependent Parent' field has a value.</figcaption>
</figure>

maxCount (Checkboxes Only)

Default: null

Checkbox groups with the maxCount property set to an integer will fail validation if the number of checked inputs is over this limit.

See here for more information on how to apply options to checkboxes.

minCount (Checkboxes Only)

Default: 1

Checkbox groups with the minCount property set to an integer will fail validation if the number of checked inputs is under this limit.

See here for more information on how to apply options to checkboxes.

optional

Default: false

Fields with the optional property set to true will pass vaidation when blank. They still will be processed for validation when they contain a value based on the input type attribute, or if a user pattern is specified.

pattern

Default: null

A regular expression to test the field's value against. This string is also copied and added to the field as a native pattern attribute. It is recommended to also provide an error message to display when the value does not match pattern by providing a patternMessage.

patternMessage

Default: null

An error message to display when the field fails the custom validation test provided by pattern.

If not provided with pattern the field will be marked as invalid, but no message will output when the pattern does not match.

valueSrc

Default: null

Used to validate custom form controls. See here for more information.