Thursday, January 10, 2019

Better HTML5 Input Fields

HTML5 boldly came with the claim that it would offer more realistic input fields able to serve the needs of web applications. Apparently the HTML5 standards supports a long list of input types well beyond the historical short group of text, password, checkbox, radio, file, and button form input fields. The new list includes date/time specific input fields and types of input fields to accept ad hoc data such email addresses, URLs, phone numbers and plain numbers. The syntax of the INPUT element was extended to make up for new features. For example, the date input field now recognizes two extra attributes such as min and max to denote the earliest and latest dates that the control can accept.

It’s not gold all that shines though. Much of the work is left to browsers, and browsers, for a number of reasons, don’t typically hard-code much of the behavior that one might expect. In particular, no browser would check the validity of the input the user entered until the form is submitted. This means that users are apparently allowed to type any sort of characters in, say, a numeric input field just to find out that the input is invalid when they push the submit button. The same thing happens for phone numbers, websites, email addresses, dates and the like.

Finding a workaround is not a hard task, but it takes a bit of JavaScript code. Worse yet, it takes a bit of JavaScript code that must be written over and over again for every single use of any of the rich HTML5 input fields. This article intends to provide a jQuery-based JavaScript library—just a bunch of selectors actually—that when loaded into a layout page will extend a standard and more specific behavior to some HTML5 input fields. In particular, the library will automatically pre-process a number of HTML5 input fields adding to them the ability to react to invalid input on the blur event. The benefit for web developers is that you only need to reference the JavaScript library and that’s it—all will happen silently and effectively.

Although the library is provided as a jQuery extension, it wouldn’t take much to adapt jQuery selectors to plain DOM selectors and make it work even without jQuery.

Covered Use Cases

The HTML form input fields being modified in this article is described in the table below that lists the type of the input field and the implemented changes.

INPUT

DESCRIPTION

<input type=”email” />

Validate the email address as the user tabs out based on a fixed regular expression

<input type=”url” />

Validate the URL as the user tabs out based on a fixed regular expression

<input type=”tel” />

Validate the phone number based as the user tabs out based on user-defined regular expression

<input type=”number” />

Forces the user to only enter digits and automatically refuses any number outside the min/max range.

<input type=”password” />

Adds a button to toggle the type of the field between password and text allowing users to read the current password in clear.

<input type=”text” />

Honors the pattern attribute when the user tabs out of the field.

Let’s find out more and proceed case by case. Note that to exercise more control over validation phase of HTML5 forms, you might want to check out the spec of the validity browser API.

Validating Email Addresses

Certainty vanishes suddenly when it comes to validating emails. Every application, depending on the locales, may have different edge cases. HTML5 browsers do have a built-in engine to validate email addresses but, as mentioned, validation is only triggered upon form submission. If you want to have the email validated as soon as the user tabs out of the input field, then some ad hoc JavaScript is the only option left.

Here’s the skeleton of the JavaScript you want to have in place in all of your web pages with some HTML forms.

$("input[type=email]").on("blur",
    function () {
        var email = $(this).val();
        var re = / ... /;
        var success = re.test(email);
        if (success)
            $(this).removeClass("is-invalid");
        else
            $(this).addClass("is-invalid");
    });

A handler for the DOM blur event is set up for any INPUT element with the type attribute set to email. The jQuery-based body of the handler first grabs the current value of the input field and then checks it against a provided regular expression.

// YBQ FORMS
var success = re.test(email);

If the check is successful, then input field removes any is-invalid CSS class it may have. Otherwise, the input field just adds the is-invalid CSS class. Note that is-invalid is a Bootstrap 4 specific class aimed at rendering input fields with a red border to notify the invalid state. Needless to say, if you do not wish to use Bootstrap 4 to style your web page, then you’re welcome to apply any other CSS transformation to the DOM that reflects the invalid state of the element.

There’s a bit more you want to do, however, in the blur handler. First, you might want to deal with an empty or whitespace content. A blank field is valid or not? The idea here is to use the HTML5 required attribute to settle the question. If required is found, then the blank content should be parsed against the regular expression, otherwise, any further check is skipped and the content is validated.

$("input[type=email]").on("blur",
    function () {
        var email = $.trim($(this).val());
        var required = $(this).attr("required") != null;
        if (email.length === 0 && !required)
            return;

        // more code ...
});

Second, you can use the HTML5 pattern attribute to indicate a form-specific regular expression for the handler to process. If the attribute is not specified then the handler uses a built-in regular expression. Check out the source code for details.

$("input[type=email]").on("blur",
    function () {
        var email = $.trim($(this).val());
        var required = $(this).attr("required") != null;
        if (email.length === 0 && !required)
            return;
        var re = / ... /;
        var pattern = $(this).attr("pattern");
        if (pattern != null) {
            re = new RegExp($(this).attr("pattern"));
        }
        var success = re.test(email);
        
        // more code ...
});

The net effect is shown in the figure below. Note that most browsers also provide detailed error messages in the form of tooltips. Note also that the error message displayed through the tooltip is based on the browser’s internal email regular expression which might be different from the one hard-coded in the library or that you may have set through the pattern attribute. To turn off the tooltip, you just set the title attribute of the INPUT field to the empty string or anything else you like.

Validating URLs

Validating a URL input field poses exactly the same challenges as validating an email address as in the HTML5 spec. The type url supports the same attributes as the type email. Here’s then the code you need to have in place.

$("input[type=url]").on("blur",
    function () {
        var url = $.trim($(this).val());
        var required = $(this).attr("required") != null;
        if (url.length === 0 && !required)
            return;
        var re = / ... /;
        var pattern = $(this).attr("pattern");
        if (pattern != null) {
            re = new RegExp($(this).attr("pattern"));
        }

        var success = re.test(url);
        if (success)
            $(this).removeClass("is-invalid");
        else
            $(this).addClass("is-invalid");
});

For details about the default regular expression used to check the scheme of the URL refer to the source code.

Validating Phone Numbers

The code to extend input fields of type tel is slightly simpler and for a good reason: there’s nearly no chance that someone can come up with a sufficiently agreed, default regular expression for validating phone numbers. Because of this, the sample library doesn’t even attempt to support a default regular expression and just uses any expressions provided through the HTML5 pattern attribute. This makes the overall blur handler a bit shorter, as below.

$("input[type=tel]").on("blur",
    function () {
        var tel = $(this).val();
        var required = $(this).attr("required") != null;
        if (tel.length === 0 && !required)
            return;
 
        var re = new RegExp($(this).attr("pattern"));
        var success = re.test(tel);
        if (success)
            $(this).removeClass("is-invalid");
        else
            $(this).addClass("is-invalid");
    });

This said, what would be at least a good candidate to validate phone numbers? Here’s my shot at it:

<div class="form-group">
    <label for="phone">Phone</label>
    <input type="tel" class="form-control" 
           id="phone" name="phone" 
   pattern="[+][0-9]{1,3} [0-9]{3}[\s-][0-9]{4}[\s-][0-9]{3}"
           placeholder="+x xxx-xxxx-xxx">
    <div class="invalid-feedback">
        Phone number must be +x xxx-xxxx-xxx
    </div>
</div>

The phone number matched by the expression above is +X XXX-XXXX-XXX. To be precise, the dash (-) can also be replaced with a blank character. The international country code is up to three digits and number is grouped in three chunks of 3, 4, 3 digits respectively.

Accepting Numbers

Personally, I just hate to be able to type anything into a textbox only to receive a warning through a tooltip and an error message later. If the input field is declared to be of type number, then users should never be able to type in anything but digits. Here’s how to do it.

$("input[type=number]")
    .on("keypress",
        function (event) {
            if (event.charCode < 48 || event.charCode > 57) {
                event.preventDefault();
                return false;
            }
        })
    .on("keyup", function () {
        var buffer = $(this).val();
        var maxLength = parseInt($(this).attr("maxlength"));
        if (buffer.length > maxLength) {
            $(this).val("");
            return false;
        }
        var minVal = parseInt($(this).attr("min"));
        var maxVal = parseInt($(this).attr("max"));
        var number = parseInt(buffer);
        if (number < minVal || number > maxVal) {
            $(this).val("");
            return false;
        }
        return true;
    });

There are two handlers: one for keypress and one for keyup. The former refuses any keyboard button different from 0-9 digits. Admittedly, this implementation doesn’t support anything but positive integers. The keyup handler, instead, ensures that any number typed falls within the defined min/max range.

HTML5, in fact, allows you to set a min and a max attribute on numeric fields to delimit the range of feasible values. However, those boundaries are not checked until the form is submitted. With the keyup implementation, instead, the current value of the buffer is turned into an integer, checked against the min/max interval and emptied if it falls outside. In this way, the input field only returns integers (or blanks) in the given range.

In addition, the keyup handler also ensures that no more digits than maxlength are ever typed. The point is that, even though the range is, say, 1-20 and no value outside 1-20 will ever be allowed when tabbing out, then there’s no reason for the user to be able to type in more than 2 digits.

Extended Password Input Fields

The HTML5 spec adds a couple of features on password input fields that alone reduce the need for validating the password at least on the client. They are minlength and maxlength attributes, whose meaning is quite self-explanatory. In addition, HTML5 password input fields also support the pattern attribute which can be set to a JavaScript acceptable regular expression. In all these cases, though, remains the issue that any validation is performed when it might be annoying for users to note—upon form submission. Here’s how to add prompt notification of min/max length violation in the proposed password.

$("input[type=password]").on("blur",
    function() {
        var pswd = $.trim($(this).val());
        var minLength = parseInt($(this).attr("minlength"));
        var maxLength = parseInt($(this).attr("maxlength"));
        if (isNaN(minLength)) {
            minLength = 0;
        }
        if (isNaN(maxLength)) {
            maxLength = 100;
        }
        if (pswd.length < minLength || 
            pswd.length > maxLength)
            $(this).addClass("is-invalid");
        else
            $(this).removeClass("is-invalid");
    });

Quite simply, the blur handler captures the value of the minlength and maxlength attributes, turns them into numbers and compares with the actual length of the password buffer. Leading and trailing blanks are automatically removed. The password field also supports a pattern attribute for you to add some regular expressions to parse the proposed password. To add support for the pattern, just copy the same code seen above for email and URL fields.

One more thing you can do for password fields is adding—automatically—a button to switch between the password and text mode so that the password can be seen in clear.

The necessary code is shown below. You just add it to the input field reference side by side with the blur handler.

$("input[type=password]").each(function() {
    $(this)
        .add(
            "<span class='input-group-btn'>" +
            "<button type='button' class='btn btn-primary' 
             onclick='__togglePswdView(this)'>" +
            "<i class='fa fa-eye'></i></button></span>")
        .wrapAll("<div class='input-group' />");
}).on("blur", function() {
    // blur handler body
});

The code just uses the Bootstrap 4 markup for input button groups and attached some plain JavaScript code to toggle the value of the password’s type attribute.

function __togglePswdView(elem) {
    var pswd = $(elem).closest("div").find("input");
    var type = $(pswd).attr("type");
    if (type === "password")
        pswd.attr("type", "text");
    else
        pswd.attr("type", "password");
}

Controlling Input in Text Fields

Sometimes input fields are only allowed to take digits, letters and perhaps a few extra chars. You can use the pattern attribute set to a regular expression but if you do so you also need some of the aforementioned JavaScript to force the expression to be matched during the blur event.

$("input[type=text]").on("blur",
    function () {
        var text = $(this).val();
        var re = /(?:)/;
        var pattern = $(this).attr("pattern");
        if (pattern != null) {
            re = new RegExp($(this).attr("pattern"));
        }
        var success = re.test(text);
        if (success)
            $(this).removeClass("is-invalid");
        else
            $(this).addClass("is-invalid");
    });

In this way, to force an alphanumeric input, you only need the following regular expression.

<input type="text" class="form-control" 
       id="username" name="username"
       pattern="[a-zA-Z0-9]" />

Summary

HTML5 is now widely supported across modern browsers, but it pays for the goal of being a broad specification. As far as INPUT fields are concerned, there are plenty of options and a good enough range of attributes. The only problem is when those attributes are honored. Validation always takes place when the form is submitted, but this is not ideal for most scenarios when you want to give users immediate feedback about what they may have been doing wrong. In this case, JavaScript is necessary and the Ybq free library that comes with this article makes it trivially easy to trigger validation when and how you most need it. The source code is available here.

 

The post Better HTML5 Input Fields appeared first on Simple Talk.



from Simple Talk http://bit.ly/2VJ0Lzt
via

No comments:

Post a Comment