Hoppa till innehåll

Validera formulär med HTML5 och JavaScript

I det här inlägget visar jag hur du kan anpassa webbläsarens inbyggda HTML5-formulärvalidering med JavaScript och CSS. Du kommer att kunna validera från JavaScript, lägga till anpassade felmeddelanden, utföra fjärrvalidering, utföra bekräftelsevalidering, utföra filvalidering och tillämpa anpassad design för felmeddelanden.

Vi kommer att använda inbyggd formulärvalidering (HTML5) så mycket som möjligt, det är snabbare än JavaScript-validering och det innebär mindre arbete för oss. Vi behöver JavaScript, Constraint Validation API och CSS för att lägga till fler regler, anpassa meddelanden och designa felmeddelanden.

Denna plugin för HTML5-validering är utformad så att du har möjlighet att lägga till några anpassade felmeddelanden och att designa om några felmeddelanden. Inbyggda felmeddelanden i webbläsaren kommer att användas om du inte lägger till anpassade felmeddelanden och inbyggd styling kommer att användas om du inte lägger till egna behållare för felmeddelanden.

Det här tillägget har testats och fungerar med Google Chrome (75.0.3770.100), Mozilla Firefox (67.0.4) och Microsoft Edge (42.17134.1.0), detta utan någon polyfill. Om du vill stödja äldre webbläsare kan du läsa vårt inlägg om hur man kan transpilera och komplettera JavaScript. Vi måste transpilera koden och lägga till polyfills för att få denna plugin att fungera i Internet Explorer (11.829.17134.0), vi skriver om detta i slutet av det här inlägget.

JavaScript

Den här är koden hanterar alla former av validering, validering kan utlösas från en händelse (submit) eller genom att anropa den offentliga funktionen som heter valid.

var annytab = annytab || {};
annytab.validation = (function ()
{
    'use_strict';

    // Variables
    var forms = document.getElementsByTagName('form');

    // Loop forms
    for (var i = 0; i < forms.length; i++) {

        // Set forms to not validate, this code will handle validation
        forms[i].noValidate = true;

        // Add submit listener
        window.onload = forms[i].addEventListener('submit', async function (event) {

            // Prevent the form from submitting
            event.preventDefault();

            // Validate a form
            if (await validate(this) === true)
            {
                // Submit the form
                this.submit();
            }

        }, false);

        // Get elements in the form
        var elements = forms[i].querySelectorAll('input, select, textarea');

        // Add listener for elements
        for (var j = 0; j < elements.length; j++)
        {
            // Add input listeners
            elements[j].addEventListener('keydown', removeValidationErrors, false);
            elements[j].addEventListener('mousedown', removeValidationErrors, false);
        }

    } // End of for (var i = 0; i < forms.length; i++)

    // Remove validation errors
    function removeValidationErrors(event)
    {
        // Variables
        var error_output = null;

        // Find equalto element
        var confirmation = event.target.form.querySelector('[data-validation-equalto="' + event.target.getAttribute('name') + '"]');

        // Remove confirmation error
        if (confirmation !== null)
        {
            error_output = event.target.form.querySelector('[data-error-for="' + confirmation.name + '"]');
            if (error_output !== null) { error_output.innerHTML = ''; }
        }

        // Remove all errors for this element
        error_output = event.target.form.querySelector('[data-error-for="' + event.target.getAttribute('name') + '"]');
        if (error_output !== null) { error_output.innerHTML = ''; }

        // Remove IE 11 errors
        removeErrorsIE11(event.target.form);

    } // End of the removeValidationErrors method

    // Validate a form
    async function validate(form)
    {
        // Get elements in the form
        var elements = form.querySelectorAll('input, select, textarea');

        // Remove IE 11 errors
        removeErrorsIE11(form);

        // Create a focus flag
        var focus = false;

        // Loop elements
        for (i = 0; i < elements.length; i++)
        {
            // Get the control
            var ctrl = elements[i];

            // Check if the control should be validated
            if (ctrl.willValidate === false) { continue; }

            // Get error message container
            var error_output = form.querySelector('[data-error-for="' + ctrl.getAttribute('name') + '"]');

            // Get custom validation attributes
            var data_validation_remote = ctrl.getAttribute('data-validation-remote');
            var data_validation_equalto = ctrl.getAttribute('data-validation-equalto');
            var data_validation_file = ctrl.getAttribute('data-validation-file');

            // Get custom error attributes
            var data_error_input = ctrl.getAttribute('data-error-input');
            var data_error_pattern = ctrl.getAttribute('data-error-pattern');
            var data_error_range = ctrl.getAttribute('data-error-range');
            var data_error_step = ctrl.getAttribute('data-error-step');
            var data_error_length = ctrl.getAttribute('data-error-length');
            var data_error_type = ctrl.getAttribute('data-error-type');
            var data_error_required = ctrl.getAttribute('data-error-required');
            var data_error_remote = ctrl.getAttribute('data-error-remote');
            var data_error_equalto = ctrl.getAttribute('data-error-equalto');
            var data_error_file = ctrl.getAttribute('data-error-file');
            var data_error_datalist = ctrl.getAttribute('data-error-datalist');

            // Reset custom validation
            ctrl.setCustomValidity('');

            // Check for errors
            if (ctrl.validity.badInput === true) // parsing error
            {
                if (data_error_input !== null) { ctrl.setCustomValidity(data_error_input); }
            }
            else if (ctrl.validity.patternMismatch === true) // pattern
            {
                if (data_error_pattern !== null) { ctrl.setCustomValidity(data_error_pattern); }
            }
            else if (ctrl.validity.rangeOverflow === true || ctrl.validity.rangeUnderflow === true) // max value or min value
            {
                if (data_error_range !== null) { ctrl.setCustomValidity(data_error_range); }
            }
            else if (ctrl.validity.stepMismatch === true) // step value
            {
                if (data_error_step !== null) { ctrl.setCustomValidity(data_error_step); }
            }
            else if (ctrl.validity.tooLong === true || ctrl.validity.tooShort) // max length or min length
            {
                if (data_error_length !== null) { ctrl.setCustomValidity(data_error_length); }
            }
            else if (ctrl.validity.typeMismatch === true) // input type error
            {
                if (data_error_type !== null) { ctrl.setCustomValidity(data_error_type); }
            }
            else if (ctrl.validity.valueMissing === true) // required
            {
                if (data_error_required !== null) { ctrl.setCustomValidity(data_error_required); }
            }
            else if (data_validation_equalto !== null) // confirmation
            {
                if (equaltoValidation(ctrl, data_validation_equalto) === false) { ctrl.setCustomValidity(data_error_equalto); }
            }
            else if (data_validation_file !== null) // File
            {
                if (fileValidation(ctrl, data_validation_file) === false) { ctrl.setCustomValidity(data_error_file); }
            }
            else if (data_error_datalist !== null) // Datalist
            {
                if (datalistValidation(ctrl) === false) { ctrl.setCustomValidity(data_error_datalist); }
            }
            else if (data_validation_remote !== null) {
                // Perform remote validation
                if (await remoteValidation(ctrl, data_validation_remote) === false) { ctrl.setCustomValidity(data_error_remote); }
            }

            // Set error message in custom control or report validity
            if (ctrl.validationMessage !== '' && error_output !== null) {
                error_output.innerHTML = ctrl.validationMessage;

                // Set focus to the first element
                if (focus === false) { focus = true; ctrl.focus(); }
            }
            else if (ctrl.validationMessage !== '' && ctrl.reportValidity) {
                ctrl.reportValidity();
            }
            else if (ctrl.validationMessage !== '') {
                // IE 11
                ctrl.insertAdjacentHTML('afterend', '<div class="validation-error ie-11-error-output">' + ctrl.validationMessage + '</div>');
            }

        } // for (i = 0; i < elements.length; i++)

        // Return true or false
        return form.checkValidity();

    } // End of the validate method

    // Perform equalto validation
    function equaltoValidation(ctrl, other_field)
    {
        // Get the value of the other field
        var other_value = document.getElementsByName(other_field)[0].value;

        // Check if values are different
        if (ctrl.value !== other_value) {
            return false;
        }

        // Return true
        return true;

    } // End of the equaltoValidation method

    // Perform file validation
    function fileValidation(ctrl, max_size)
    {
        // Make sure that there is files
        if (ctrl.files.length <= 0) {
            return true;
        }

        // Check accept attribute
        var accepts = ctrl.getAttribute('accept') !== null ? ctrl.getAttribute('accept').toLowerCase().replace(' ', '').split(',') : null;

        // Loop files
        for (var i = 0; i < ctrl.files.length; i++) {
            // Get the file extension
            var extension = ctrl.files[i].name.substring(ctrl.files[i].name.lastIndexOf('.')).toLowerCase();

            // Check for errors
            if (accepts !== null && accepts.includes(extension) === false) {
                return false;
            }
            else if (max_size !== null && max_size > 0 && ctrl.files[i].size >= max_size) {
                return false;
            }
        }

        // Return true
        return true;

    } // End of the fileValidation method

    // Perform a datalist validation
    function datalistValidation(ctrl)
    {
        // Loop options
        for (var i = 0; i < ctrl.list.options.length; i++) {
            if (ctrl.value === ctrl.list.options[i].value) {
                return true;
            }
        }

        // Return false
        return false;

    } // End of the datalistValidation method

    // Perform remote validation
    async function remoteValidation(ctrl, input)
    {
        // Return a promise
        return new Promise((resolve, reject) => {

            // Get input values
            var values = input.split(',');
            var uri = values[0].trim();
            var fields = [];
            fields.push(ctrl.getAttribute('name'));
            for (var i = 1; i < values.length; i++) { fields.push(values[i].trim()); }

            // Create form data
            var fd = new FormData();
            for (i = 0; i < fields.length; i++) { fd.append(fields[i], document.getElementsByName(fields[i])[0].value); }

            var xhr = new XMLHttpRequest();
            xhr.open('POST', uri, true);
            xhr.onload = function () {

                // Check if the response is successful
                if (xhr.status === 200) {
                    // Return a success response
                    if (xhr.response === 'false') { resolve(false); } else { resolve(true); }
                }
                else {
                    // Return a reject response
                    reject(xhr.status + ' ' + xhr.statusText);
                }
            };
            xhr.onerror = function () {
                // Return a reject response
                reject('There was a network error.');
            };
            xhr.send(fd);
        });

    } // End of the remoteValidation method

    // Remove IE 11 errors
    function removeErrorsIE11(form)
    {
        var ie_errors = form.querySelectorAll('.ie-11-error-output');
        for (var i = 0; i < ie_errors.length; i++) { ie_errors[i].remove(); }

    } // End of the removeErrorsIE11 method

    // Public methods
    return {
        valid: async function (form) {
            return await validate(form);
        }
    };

})();

Exempelformulär

Det här är ett formulär med html-syntax och razor-syntax, det innehåller inmatningselement och en metod för att skicka datan i formuläret. Du behöver inte posta formuläret med JavaScript, du kan istället använda en inmatningsknapp (submit).

@{
    // Set the layout for the page
    Layout = "/Views/shared_admin/_standard_layout.cshtml";

    // Get form data
    WebDomain current_domain = ViewBag.CurrentDomain;
    KeyStringList tt = ViewBag.TranslatedTexts;
    AdministratorDocument post = ViewBag.Post;

    // Get translated texts
    string administrator_tt = tt.Get("administrator");
    string new_tt = tt.Get("new");
    string edit_tt = tt.Get("edit");
    string id_tt = tt.Get("id");
    string username_tt = tt.Get("username");
    string password_tt = tt.Get("password");
    string confirm_password_tt = tt.Get("confirm") + " " + tt.Get("password").ToLower();
    string admin_role_tt = tt.Get("role");
    string email_tt = tt.Get("email");
    string save_tt = tt.Get("save");
    string cancel_tt = tt.Get("cancel");

    // Set the title for the page
    if (post.admin_username == "")
    {
        ViewBag.Title = administrator_tt + " - " + new_tt.ToLower();
    }
    else
    {
        ViewBag.Title = administrator_tt + " - " + edit_tt.ToLower();
    }
}

@*Title*@
<h1>@ViewBag.Title</h1>

@*Menu*@
@await Html.PartialAsync("/Views/admin_administrators/_form_menu.cshtml")

@*Main form*@
<form id="inputForm" action="/admin_administrators/edit" method="post" enctype="multipart/form-data">

    @*Hidden data*@
    @Html.AntiForgeryToken()

    @*Id*@
    <div class="annytab-form-label">@id_tt</div>
    <input name="txtId" type="text" class="annytab-form-control" tabindex="-1" readonly value="@post.id" />

    @*Text*@
    <div class="annytab-form-label">@username_tt</div>
    <input name="txtUsername" type="text" class="annytab-form-control" value="@post.admin_username" placeholder="@username_tt" autofocus required
           data-error-required="You must enter a username!" data-validation-remote="/admin_administrators/verify_username,txtId"
           data-error-remote="The username is already in use!" maxlength="10" minlength="2" />
    <div class="validation-error" data-error-for="txtUsername"></div>

    @*Confirmation*@
    <div class="annytab-form-label">@password_tt</div>
    <input name="txtPassword" type="password" class="annytab-form-control" value="" placeholder="@password_tt" />
    <input name="txtConfirmPassword" type="password" class="annytab-form-control" value="" placeholder="@confirm_password_tt"
           data-validation-equalto="txtPassword" data-error-equalto="Passwords does not match!" />
    <div class="validation-error" data-error-for="txtConfirmPassword"></div>

    @*Select*@
    <div class="annytab-form-label">@admin_role_tt</div>
    <select name="selectAdminRole" class="annytab-form-control" required>
        <!option value="">--- Select a role ---</!option>
        <!option value="Administrator" @(post.admin_role == "Administrator" ? "selected" : "")>Administrator</!option>
        <!option value="Editor" @(post.admin_role == "Editor" ? "selected" : "")>Editor</!option>
        <!option value="Translator" @(post.admin_role == "Translator" ? "selected" : "")>Translator</!option>
    </select>

    @*Email*@
    <div class="annytab-form-label">Email</div>
    <input name="txtEmail" type="email" class="annytab-form-control" value="@post.admin_email" placeholder="my@email.com" required data-error-input="Bad input!" />

    @*Multiple emails*@
    <div class="annytab-form-label">Multiple emails</div>
    <input name="txtEmail" type="email" class="annytab-form-control" value="" placeholder="my@email.com, your@email.se" multiple required />

    @*Hidden field*@
    <div class="annytab-form-label">Hidden field</div>
    <input name="txtHidden" type="text" class="annytab-form-control" style="display:none;" value="ee" required data-error-required="Hidden field is required!" />
    <div class="validation-error" data-error-for="txtHidden"></div>

    @*Date*@
    <div class="annytab-form-label">Date</div>
    <input name="txtDate" type="date" class="annytab-form-control" value="" />

    @*Number*@
    <div class="annytab-form-label">Number</div>
    <input name="txtNumber" type="number" class="annytab-form-control" value="" min="1" max="10" />

    @*Range*@
    <div class="annytab-form-label">Range</div>
    <input name="txtRange" type="range" class="annytab-form-control" value="" min="1" max="10" step="1" />

    @*Pattern*@
    <div class="annytab-form-label">True/false</div>
    <input name="txtPattern" type="text" class="annytab-form-control" value="" pattern="^(?:tru|fals)e$" data-error-pattern="true or false only!" />

    @*Textarea*@
    <div class="annytab-form-label">Textarea</div>
    <textarea name="txtContents" required></textarea>

    <div class="annytab-basic-space"></div>

    @*Button panel*@
    <div class="annytab-form-button-container">
        <input type="button" class="annytab-form-button btn-disablable" value="@save_tt" onclick="sendForm(document.getElementById('inputForm'))" />
        @*<input type="submit" class="annytab-form-button" value="@save_tt" />*@
        <input type="button" class="annytab-form-button btn-disablable" value="@cancel_tt" onclick="location.href='/admin_administrators'" />
    </div>

</form>

@section scripts {
    <script>

        // Submit a form
        async function sendForm(form)
        {
            // Disable buttons
            disableButtons();

            // Make sure that the form is valid
            if (await annytab.validation.valid(form) === false) { enableButtons(); return false; }

            // Get form data
            var fd = new FormData(form);

            // Post form data
            var xhr = new XMLHttpRequest();
            xhr.open('POST', form.getAttribute('action'), true);
            xhr.onload = function () {
                if (xhr.status === 200)
                {
                    // Get the response
                    var data = JSON.parse(xhr.response);

                    // Check the success status
                    if (data.success === true)
                    {
                        // Output a success message
                        toastr['success'](data.message);
                    }
                    else
                    {
                        // Output error information
                        toastr['error'](data.message);
                    }
                }
                else
                {
                    // Output error information
                    toastr['error'](xhr.status + " - " + xhr.statusText);
                }

                // Enable buttons
                enableButtons();

            };
            xhr.onerror = function ()
            {
                // Output error information
                toastr['error'](xhr.status + " - " + xhr.statusText);

                // Enable buttons
                enableButtons();
            };
            xhr.send(fd);

        } // End of the sendForm method

        // Disable buttons
        function disableButtons()
        {
            var buttons = document.getElementsByClassName('btn-disablable');
            for (var i = 0; i < buttons.length; i++) {
                buttons[i].setAttribute('disabled', true);
            }

        } // End of the disableButtons method

        // Enable buttons
        function enableButtons()
        {
            var buttons = document.getElementsByClassName('btn-disablable');
            for (var i = 0; i < buttons.length; i++) {
                setTimeout(function (button) { button.removeAttribute('disabled'); }, 1000, buttons[i]);
            }

        } // End of the enableButtons method

    </script>
}

Design

Du kan designa felmeddelanden och tillämpa stilar på ogiltiga och giltiga element. Det här är ett element som används för att mata ut felmeddelanden för elementet txtUsername.

<div class="validation-error" data-error-for="txtUsername"></div>

Här visas CSS för att bestämma utseende på ovanstående kontroll och för att sätta stilen för ogiltiga element.

/* Validation */
.validation-error {
    display: block;
    font-weight: normal;
    font-size: 16px;
    line-height: 16px;
    color: #f00000;
    padding: 5px 5px 10px 0;
    margin: 0;
}
input:invalid, textarea:invalid{
    border: 1px solid #f00000;
}
input:disabled, button:disabled, input:disabled:hover, button:disabled:hover {
    resize: none;
    outline: none;
    color: #000000;
    background-color: #ddd;
    border-color: #ddd;
    cursor: default;
}

En visuell bild av ett formulär visas nedan. Inbyggd styling tillämpas om ett felmeddelandeelement inte finns.

Html5 validering

Fjärrvalidering

Fjärrvalidering innebär att vi anropar en servermetod för att få sant eller falskt till svar. Om du vill lägga till fjärrvalidering måste du lägga till ett data-validation-remote attribut och ett attribut för data-error-remote i ett element. Attributet data-validation-remote skall innehålla en url och namnen för ytterligare fält, data avgränsas med kommatecken (,).

<input name="txtUsername" type="text" class="annytab-form-control" value="" data-validation-remote="/admin_administrators/verify_username,txtId" data-error-remote="The username is already in use!" />

Du behöver också en servermetod som anropas av valideringskoden.

HttpPost]
[Authorize(Roles = "Administrator")]
public async Task<IActionResult> verify_username(IFormCollection collection)
{
    // Get the current domain
    WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

    // Get translated texts
    KeyStringList tt = await this.static_text_repository.GetFromCache(current_domain.back_end_language_code);

    // Get form data
    string id = collection["txtId"].ToString();
    string username = collection["txtUsername"].ToString();

    // Get a administrator on user name
    ModelItem<AdministratorDocument> admin_username_model = await this.administrator_repository.GetByUsername(username);

    // Check if the username exists already
    if (admin_username_model.item != null && id != admin_username_model.item.id)
    {
        // Return false
        return Ok(false);
        //return Json(data: false);
    }

    // Return success
    return Ok(true);
    //return Json(data: true);

} // End of the verify_username method

Bekräftelsevalidering

Bekräftelse validering (equalto) kräver att du lägger till ett data-validation-equalto attribut och ett data-error-equalto i det element som används för att bekräfta ett annat inmatningselement.

<input name="txtConfirmPassword" type="password" class="annytab-form-control" value="" data-validation-equalto="txtPassword" data-error-equalto="Passwords does not match!" />

Filvalidering

Filvalidering kräver att du lägger till attributet data-validation-file med en maximal filstorlek i bytes och ett data-error-file attribut med ett felmeddelande. Sätt den maximala filstorleken till -1 om du bara vill validera avseende filtillägg. Du kan lägga till ett accept attribut för att begränsa uppladdningen till bara tillåtna filtillägg.

<div class="annytab-form-label">File</div>
<input type="file" accept=".jpg, .PNG" data-validation-file="262144" data-error-file="Max 256 KiB per file, must be .jpg or .png!" />

Validering av datalista

En datalista kan användas för att kombinera sökning och val. Elementet datalista stöds inte i äldre webbläsare, ladda ner en datalist-polyfill för att lägga till den här funktionaliteten i inkompatibla webbläsare. Lägg till attributet data-error-datalist om du vill se till att en användare bara kan välja ett av de angivna alternativen.

<input name="selectLanguage" type="text" value="sv"
        placeholder="Select language" list="dtLanguagues"
        data-error-datalist="You must select a valid language code!" />
<datalist id="dtLanguagues">
    <option value="sv">Swedish</option>
    <option value="no">Norwegian</option>
    <option value="da">Danish</option>
    <option value="en">English</option>
    <option value="fi">Finnish</option>
</datalist>

Internet Explorer 11 (äldre webbläsare)

Internet Explorer 11 och äldre versioner av webbläsare stöder inte Async/Await, XMLHttpRequest, Array.prototype.includes, Promise och Element.prototype.remove. Vi ska kompilera JavaScript som TypeScript genom att använda JavaScript Transpiler av Mads Kristensen. Vi lägger till en tsconfig.json-fil med innehållet som visas nedan i roten för vårt projekt. Inkluderade filer transpileras till nya filer till den angivna katalogen (outDir). Filer i den transpilerade mappen kan minifieras och du kan själv bestämma i vilken ordning filerna ska laddas in i en webbläsare.

{
  "compileOnSave": true,
  "files": [
    "wwwroot/js/annytab.html5.validation.js",
    "wwwroot/js/annytab.effects.js"
  ],
  "compilerOptions": {
    "allowJs": true,
    "sourceMap": false,
    "target": "es5",
    "outDir": "wwwroot/tjs-typescript"
  }
}

Transpilerade filer skapas i mappen outDir varje gång vi bygger vårt projekt eller när en av filerna ändras.

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var annytab = annytab || {};
annytab.validation = (function () {
    'use_strict';
    // Variables
    var forms = document.getElementsByTagName('form');
    // Loop forms
    for (var i = 0; i < forms.length; i++) {
        // Set forms to not validate, this code will handle validation
        forms[i].noValidate = true;
        // Add submit listener
        window.onload = forms[i].addEventListener('submit', function (event) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            // Prevent the form from submitting
                            event.preventDefault();
                            return [4 /*yield*/, validate(this)];
                        case 1:
                            // Validate a form
                            if ((_a.sent()) === true) {
                                // Submit the form
                                this.submit();
                            }
                            return [2 /*return*/];
                    }
                });
            });
        }, false);
        // Get elements in the form
        var elements = forms[i].querySelectorAll('input, select, textarea');
        // Add listener for elements
        for (var j = 0; j < elements.length; j++) {
            // Add input listeners
            elements[j].addEventListener('keydown', removeValidationErrors, false);
            elements[j].addEventListener('mousedown', removeValidationErrors, false);
        }
    } // End of for (var i = 0; i < forms.length; i++)
    // Remove validation errors
    function removeValidationErrors(event) {
        // Variables
        var error_output = null;
        // Find equalto element
        var confirmation = event.target.form.querySelector('[data-validation-equalto="' + event.target.getAttribute('name') + '"]');
        // Remove confirmation error
        if (confirmation !== null) {
            error_output = event.target.form.querySelector('[data-error-for="' + confirmation.name + '"]');
            if (error_output !== null) {
                error_output.innerHTML = '';
            }
        }
        // Remove all errors for this element
        error_output = event.target.form.querySelector('[data-error-for="' + event.target.getAttribute('name') + '"]');
        if (error_output !== null) {
            error_output.innerHTML = '';
        }
        // Remove IE 11 errors
        removeErrorsIE11(event.target.form);
    } // End of the removeValidationErrors method
    // Validate a form
    function validate(form) {
        return __awaiter(this, void 0, void 0, function () {
            var elements, focus, ctrl, error_output, data_validation_remote, data_validation_equalto, data_validation_file, data_error_input, data_error_pattern, data_error_range, data_error_step, data_error_length, data_error_type, data_error_required, data_error_remote, data_error_equalto, data_error_file, data_error_datalist;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        elements = form.querySelectorAll('input, select, textarea');
                        // Remove IE 11 errors
                        removeErrorsIE11(form);
                        focus = false;
                        i = 0;
                        _a.label = 1;
                    case 1:
                        if (!(i < elements.length)) return [3 /*break*/, 15];
                        ctrl = elements[i];
                        // Check if the control should be validated
                        if (ctrl.willValidate === false) {
                            return [3 /*break*/, 14];
                        }
                        error_output = form.querySelector('[data-error-for="' + ctrl.getAttribute('name') + '"]');
                        data_validation_remote = ctrl.getAttribute('data-validation-remote');
                        data_validation_equalto = ctrl.getAttribute('data-validation-equalto');
                        data_validation_file = ctrl.getAttribute('data-validation-file');
                        data_error_input = ctrl.getAttribute('data-error-input');
                        data_error_pattern = ctrl.getAttribute('data-error-pattern');
                        data_error_range = ctrl.getAttribute('data-error-range');
                        data_error_step = ctrl.getAttribute('data-error-step');
                        data_error_length = ctrl.getAttribute('data-error-length');
                        data_error_type = ctrl.getAttribute('data-error-type');
                        data_error_required = ctrl.getAttribute('data-error-required');
                        data_error_remote = ctrl.getAttribute('data-error-remote');
                        data_error_equalto = ctrl.getAttribute('data-error-equalto');
                        data_error_file = ctrl.getAttribute('data-error-file');
                        data_error_datalist = ctrl.getAttribute('data-error-datalist');
                        // Reset custom validation
                        ctrl.setCustomValidity('');
                        if (!(ctrl.validity.badInput === true)) return [3 /*break*/, 2];
                        if (data_error_input !== null) {
                            ctrl.setCustomValidity(data_error_input);
                        }
                        return [3 /*break*/, 13];
                    case 2:
                        if (!(ctrl.validity.patternMismatch === true)) return [3 /*break*/, 3];
                        if (data_error_pattern !== null) {
                            ctrl.setCustomValidity(data_error_pattern);
                        }
                        return [3 /*break*/, 13];
                    case 3:
                        if (!(ctrl.validity.rangeOverflow === true || ctrl.validity.rangeUnderflow === true)) return [3 /*break*/, 4];
                        if (data_error_range !== null) {
                            ctrl.setCustomValidity(data_error_range);
                        }
                        return [3 /*break*/, 13];
                    case 4:
                        if (!(ctrl.validity.stepMismatch === true)) return [3 /*break*/, 5];
                        if (data_error_step !== null) {
                            ctrl.setCustomValidity(data_error_step);
                        }
                        return [3 /*break*/, 13];
                    case 5:
                        if (!(ctrl.validity.tooLong === true || ctrl.validity.tooShort)) return [3 /*break*/, 6];
                        if (data_error_length !== null) {
                            ctrl.setCustomValidity(data_error_length);
                        }
                        return [3 /*break*/, 13];
                    case 6:
                        if (!(ctrl.validity.typeMismatch === true)) return [3 /*break*/, 7];
                        if (data_error_type !== null) {
                            ctrl.setCustomValidity(data_error_type);
                        }
                        return [3 /*break*/, 13];
                    case 7:
                        if (!(ctrl.validity.valueMissing === true)) return [3 /*break*/, 8];
                        if (data_error_required !== null) {
                            ctrl.setCustomValidity(data_error_required);
                        }
                        return [3 /*break*/, 13];
                    case 8:
                        if (!(data_validation_equalto !== null)) return [3 /*break*/, 9];
                        if (equaltoValidation(ctrl, data_validation_equalto) === false) {
                            ctrl.setCustomValidity(data_error_equalto);
                        }
                        return [3 /*break*/, 13];
                    case 9:
                        if (!(data_validation_file !== null)) return [3 /*break*/, 10];
                        if (fileValidation(ctrl, data_validation_file) === false) {
                            ctrl.setCustomValidity(data_error_file);
                        }
                        return [3 /*break*/, 13];
                    case 10:
                        if (!(data_error_datalist !== null)) return [3 /*break*/, 11];
                        if (datalistValidation(ctrl) === false) {
                            ctrl.setCustomValidity(data_error_datalist);
                        }
                        return [3 /*break*/, 13];
                    case 11:
                        if (!(data_validation_remote !== null)) return [3 /*break*/, 13];
                        return [4 /*yield*/, remoteValidation(ctrl, data_validation_remote)];
                    case 12:
                        // Perform remote validation
                        if ((_a.sent()) === false) {
                            ctrl.setCustomValidity(data_error_remote);
                        }
                        _a.label = 13;
                    case 13:
                        // Set error message in custom control or report validity
                        if (ctrl.validationMessage !== '' && error_output !== null) {
                            error_output.innerHTML = ctrl.validationMessage;
                            // Set focus to the first element
                            if (focus === false) {
                                focus = true;
                                ctrl.focus();
                            }
                        }
                        else if (ctrl.validationMessage !== '' && ctrl.reportValidity) {
                            ctrl.reportValidity();
                        }
                        else if (ctrl.validationMessage !== '') {
                            // IE 11
                            ctrl.insertAdjacentHTML('afterend', '<div class="validation-error ie-11-error-output">' + ctrl.validationMessage + '</div>');
                        }
                        _a.label = 14;
                    case 14:
                        i++;
                        return [3 /*break*/, 1];
                    case 15: // for (i = 0; i < elements.length; i++)
                    // Return true or false
                    return [2 /*return*/, form.checkValidity()];
                }
            });
        });
    } // End of the validate method
    // Perform equalto validation
    function equaltoValidation(ctrl, other_field) {
        // Get the value of the other field
        var other_value = document.getElementsByName(other_field)[0].value;
        // Check if values are different
        if (ctrl.value !== other_value) {
            return false;
        }
        // Return true
        return true;
    } // End of the equaltoValidation method
    // Perform file validation
    function fileValidation(ctrl, max_size) {
        // Make sure that there is files
        if (ctrl.files.length <= 0) {
            return true;
        }
        // Check accept attribute
        var accepts = ctrl.getAttribute('accept') !== null ? ctrl.getAttribute('accept').toLowerCase().replace(' ', '').split(',') : null;
        // Loop files
        for (var i = 0; i < ctrl.files.length; i++) {
            // Get the file extension
            var extension = ctrl.files[i].name.substring(ctrl.files[i].name.lastIndexOf('.')).toLowerCase();
            // Check for errors
            if (accepts !== null && accepts.includes(extension) === false) {
                return false;
            }
            else if (max_size !== null && max_size > 0 && ctrl.files[i].size >= max_size) {
                return false;
            }
        }
        // Return true
        return true;
    } // End of the fileValidation method
    // Perform a datalist validation
    function datalistValidation(ctrl) {
        // Loop options
        for (var i = 0; i < ctrl.list.options.length; i++) {
            if (ctrl.value === ctrl.list.options[i].value) {
                return true;
            }
        }
        // Return false
        return false;
    } // End of the datalistValidation method
    // Perform remote validation
    function remoteValidation(ctrl, input) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                // Return a promise
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        // Get input values
                        var values = input.split(',');
                        var uri = values[0].trim();
                        var fields = [];
                        fields.push(ctrl.getAttribute('name'));
                        for (var i = 1; i < values.length; i++) {
                            fields.push(values[i].trim());
                        }
                        // Create form data
                        var fd = new FormData();
                        for (i = 0; i < fields.length; i++) {
                            fd.append(fields[i], document.getElementsByName(fields[i])[0].value);
                        }
                        var xhr = new XMLHttpRequest();
                        xhr.open('POST', uri, true);
                        xhr.onload = function () {
                            // Check if the response is successful
                            if (xhr.status === 200) {
                                // Return a success response
                                if (xhr.response === 'false') {
                                    resolve(false);
                                }
                                else {
                                    resolve(true);
                                }
                            }
                            else {
                                // Return a reject response
                                reject(xhr.status + ' ' + xhr.statusText);
                            }
                        };
                        xhr.onerror = function () {
                            // Return a reject response
                            reject('There was a network error.');
                        };
                        xhr.send(fd);
                    })];
            });
        });
    } // End of the remoteValidation method
    // Remove IE 11 errors
    function removeErrorsIE11(form) {
        var ie_errors = form.querySelectorAll('.ie-11-error-output');
        for (var i = 0; i < ie_errors.length; i++) {
            ie_errors[i].remove();
        }
    } // End of the removeErrorsIE11 method
    // Public methods
    return {
        valid: function (form) {
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, validate(form)];
                        case 1: return [2 /*return*/, _a.sent()];
                    }
                });
            });
        }
    };
})();

Vi måste lägga till en polyfill och transpilera JavaScript-kod som anropar vår metod valid för att kunna använda vår es5-kod. Vår metod sendForm i exemplet ovan har också kompilerats till es5 som TypeScript, den här metoden använder hjälpmetoder från den transpilerade filen.

<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?features=Array.prototype.includes%2CPromise%2CXMLHttpRequest%2CElement.prototype.remove"></script>
<script src="/tjs-typescript/annytab.html5.validation.js"></script>
<script>

    function sendForm(form)
    {
        return __awaiter(this, void 0, void 0, function () {
            var fd, xhr;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        // Disable buttons
                        disableButtons();
                        return [4 /*yield*/, annytab.validation.valid(form)];
                    case 1:
                        // Make sure that the form is valid
                        if ((_a.sent()) === false) {
                            enableButtons();
                            return [2 /*return*/, false];
                        }
                        fd = new FormData(form);
                        xhr = new XMLHttpRequest();
                        xhr.open('POST', form.getAttribute('action'), true);
                        xhr.onload = function () {
                            if (xhr.status === 200) {
                                // Get the response
                                var data = JSON.parse(xhr.response);
                                // Check the success status
                                if (data.success === true) {
                                    // Output a success message
                                    alert(data.message);
                                }
                                else {
                                    // Output error information
                                    alert(data.message);
                                }
                            }
                            else {
                                // Output error information
                                alert(xhr.status + " - " + xhr.statusText);
                            }
                            // Enable buttons
                            enableButtons();
                        };
                        xhr.onerror = function () {
                            // Output error information
                            alert(xhr.status + " - " + xhr.statusText);
                            // Enable buttons
                            enableButtons();
                        };
                        xhr.send(fd);
                        return [2 /*return*/];
                }
            });
        });

    } // End of the sendForm method

</script>

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *