Correcting user mistakes in email input with jQuery.

This post is also available in: Español (Spanish)

Previously in this blog we have seen how to suggest an email to the user.
This post will be part of a series of articles dealing with the validation of the email field.

It is worth emphasizing the importance of validating our users email, as we have commented on previous occasions, as it will be our main method of contact with our client.

When checking emails provided by users, it is common to find typos in emails such as: hotmeil.com or gmial.com, which are usually small errors due to phonetic similarities or proximity of the letters on the keyboard.

In the ol’times, we could correct these errors by hand or filter our database by and find these user errors. But nowadays, because of the regulations in the field of data protection, it is not as easy anymore.

And some of you may think “Hey! I’m doing it by hand and I have 0 problems, bro”. Well, you’re probably doing something illegal, bro.

I am not a legal expert, but I know this: If the user has entered his contact information wrong, you have lost a customer.

You do not have the right to correct an error in your customer’s email, no matter how silly that error is, and theoretically, you cannot contact the customer by any other means that they have not authorized.

So let’s see how we can help the user realize the mistakes they’ve made when writing the email.

Suggesting a change

What we are going to achieve at the end of this post, is having an email field that when the user has enters his supposed email address will check if the part of the domain is similar to that of a common email provider. And in the case that there is a similar to one of these domains, it suggests the user to change the mail he has entered for one with the error corrected.

Basically if you write xyz@hotmsil.com we want it to suggest xyz@hotmail.com and the same for other mail providers.

Here at Sonneil we love MVC, so here’s how the form, script and styles should look, in order to have the basic text fixing functionality.

Form

The only requirement of the form is that it has an email type input.

<form class="" id="contact-form" method="POST"
                enctype="application/x-www-form-urlencoded">

                <div class="form-group">
                    <input aria-describedby="name"
                        class="form-control" name="name"
                        id="name" placeholder="Name" type="text" required>
                </div>

                <div class="form-group">
                    <input aria-describedby="email" class="form-control"
                        id="email" name="email" placeholder="E-mail" type="email" required>
                </div>
                <button type="submit">Send</button>
            </form>

Script

The time has come to give it logic, let’s start by importing jQuery.

And we create the script.jscode> file that we will import into our HTML.

Here is how the <head>code> of your HTML has to look like to import jQuery and your new document script.js

<head>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
    <script src="./scripts.js"></script>
</head>

With this everything is ready to add the functionality we are looking for.

We wish that when the user has written his email, the script reviews what the user has just entered, checks if there is an error in the domain and in case there is one, suggests the user a correction to which he can click to correct the field.

We will use a jQuery plugin that will make development much easier: mailcheck

To use it, we will import the mailcheck into our project. In our case we have copied the minified script.

The plugin does most of the work, but it is up to us to make the suggestion appear and be replaced when the user selects it.

We will create the function suggestionEmailcode> which we will call to check for possible corrections. Within this function we will call the mailcheckcode> method on the email input reference.

The mailcheckcode> method has two callbacks:

  • suggested: It will be called when the plugin thinks there is an mistake. This is where we have to control how we show the suggestion to the user.
  • empty: It will be called when the plugin believes that there is no error or no possible correction. Here we should remove any suggestions we have made.

Enough talk about code and let’s see it:


/* Email correction */
/*! mailcheck v1.1.2 @licence MIT */var Mailcheck={domainThreshold:2,secondLevelThreshold:2,topLevelThreshold:2,defaultDomains:["msn.com","bellsouth.net","telus.net","comcast.net","optusnet.com.au","earthlink.net","qq.com","sky.com","icloud.com","mac.com","sympatico.ca","googlemail.com","att.net","xtra.co.nz","web.de","cox.net","gmail.com","ymail.com","aim.com","rogers.com","verizon.net","rocketmail.com","google.com","optonline.net","sbcglobal.net","aol.com","me.com","btinternet.com","charter.net","shaw.ca"],defaultSecondLevelDomains:["yahoo","hotmail","mail","live","outlook","gmx"],defaultTopLevelDomains:["com","com.au","com.tw","ca","co.nz","co.uk","de","fr","it","ru","net","org","edu","gov","jp","nl","kr","se","eu","ie","co.il","us","at","be","dk","hk","es","gr","ch","no","cz","in","net","net.au","info","biz","mil","co.jp","sg","hu","uk"],run:function(a){a.domains=a.domains||Mailcheck.defaultDomains,a.secondLevelDomains=a.secondLevelDomains||Mailcheck.defaultSecondLevelDomains,a.topLevelDomains=a.topLevelDomains||Mailcheck.defaultTopLevelDomains,a.distanceFunction=a.distanceFunction||Mailcheck.sift4Distance;var b=function(a){return a},c=a.suggested||b,d=a.empty||b,e=Mailcheck.suggest(Mailcheck.encodeEmail(a.email),a.domains,a.secondLevelDomains,a.topLevelDomains,a.distanceFunction);return e?c(e):d()},suggest:function(a,b,c,d,e){a=a.toLowerCase();var f=this.splitEmail(a);if(c&&d&&-1!==c.indexOf(f.secondLevelDomain)&&-1!==d.indexOf(f.topLevelDomain))return!1;var g=this.findClosestDomain(f.domain,b,e,this.domainThreshold);if(g)return g==f.domain?!1:{address:f.address,domain:g,full:f.address+"@"+g};var h=this.findClosestDomain(f.secondLevelDomain,c,e,this.secondLevelThreshold),i=this.findClosestDomain(f.topLevelDomain,d,e,this.topLevelThreshold);if(f.domain){g=f.domain;var j=!1;if(h&&h!=f.secondLevelDomain&&(g=g.replace(f.secondLevelDomain,h),j=!0),i&&i!=f.topLevelDomain&&""!==f.secondLevelDomain&&(g=g.replace(new RegExp(f.topLevelDomain+"$"),i),j=!0),j)return{address:f.address,domain:g,full:f.address+"@"+g}}return!1},findClosestDomain:function(a,b,c,d){d=d||this.topLevelThreshold;var e,f=1/0,g=null;if(!a||!b)return!1;c||(c=this.sift4Distance);for(var h=0;h<b.length;h++){if(a===b[h])return a;e=c(a,b[h]),f>e&&(f=e,g=b[h])}return d>=f&&null!==g?g:!1},sift4Distance:function(a,b,c){if(void 0===c&&(c=5),!a||!a.length)return b?b.length:0;if(!b||!b.length)return a.length;for(var d=a.length,e=b.length,f=0,g=0,h=0,i=0,j=0,k=[];d>f&&e>g;){if(a.charAt(f)==b.charAt(g)){i++;for(var l=!1,m=0;m<k.length;){var n=k[m];if(f<=n.c1||g<=n.c2){l=Math.abs(g-f)>=Math.abs(n.c2-n.c1),l?j++:n.trans||(n.trans=!0,j++);break}f>n.c2&&g>n.c1?k.splice(m,1):m++}k.push({c1:f,c2:g,trans:l})}else{h+=i,i=0,f!=g&&(f=g=Math.min(f,g));for(var o=0;c>o&&(d>f+o||e>g+o);o++){if(d>f+o&&a.charAt(f+o)==b.charAt(g)){f+=o-1,g--;break}if(e>g+o&&a.charAt(f)==b.charAt(g+o)){f--,g+=o-1;break}}}f++,g++,(f>=d||g>=e)&&(h+=i,i=0,f=g=Math.min(f,g))}return h+=i,Math.round(Math.max(d,e)-h+j)},splitEmail:function(a){a=null!==a?a.replace(/^\s*/,"").replace(/\s*$/,""):null;var b=a.split("@");if(b.length<2)return!1;for(var c=0;c<b.length;c++)if(""===b[c])return!1;var d=b.pop(),e=d.split("."),f="",g="";if(0===e.length)return!1;if(1==e.length)g=e[0];else{f=e[0];for(var h=1;h<e.length;h++)g+=e[h]+".";g=g.substring(0,g.length-1)}return{topLevelDomain:g,secondLevelDomain:f,domain:d,address:b.join("@")}},encodeEmail:function(a){var b=encodeURI(a);return b=b.replace("%20"," ").replace("%25","%").replace("%5E","^").replace("%60","`").replace("%7B","{").replace("%7C","|").replace("%7D","}")}};"undefined"!=typeof module&&module.exports&&(module.exports=Mailcheck),"function"==typeof define&&define.amd&&define("mailcheck",[],function(){return Mailcheck}),"undefined"!=typeof window&&window.jQuery&&!function(a){a.fn.mailcheck=function(a){var b=this;if(a.suggested){var c=a.suggested;a.suggested=function(a){c(b,a)}}if(a.empty){var d=a.empty;a.empty=function(){d.call(null,b)}}a.email=this.val(),Mailcheck.run(a)}}(jQuery);

/* Form code */
jQuery(document).ready( function () {
    function suggestionEmail(element) {
        element.mailcheck({
            suggested: function(element, suggestion) {
                if(document.getElementById("hint")) document.getElementById("hint").remove();
                var hint = document.createElement("div");
                hint.setAttribute("id", "hint");
                hint.style.height = 0;
                var html = `<p>
                                <span>
                                    ${suggestion.full}
                                </span> ?
                            </p>`;
                hint.innerHTML = html;
                hint.addEventListener("click", () => {element[0].value = suggestion.full; document.getElementById("hint").remove();});
                element.after(hint);
            },
            empty: function(element) {
                if(document.getElementById("hint")) document.getElementById("hint").remove();
            }
        })
    }

    /* Client email validation */
    jQuery("#contact-form input[type='email'].form-control").each(function () {
        jQuery(this).blur(function() {
                suggestionEmail(jQuery(this));
        })
    })


});

As you can see in the previous code, in suggestedcode> we have created <p> code> element that shows the correction of the email and the logic that replaces the input text. Plugin suggestions are accessed through the second parameter of the suggestedcode> callback, which is an object with three attributes: address (mail before the @), domain (the suggested domain) and full (which is the full mail).

In the emptycode> callback we have added that if there is a suggestion in the DOM remove it, because if it is in emptycode> it means that the error has been corrected.

If you want more customization, such as adding your own domain list, differentiating suggestions by domain level, limiting the length of the suggestions, etc. you can consult the plugin’s documentation.

Styles

To finish we are going to add some simple styles, it is important to control that the suggestion of the email is shown correctly in our form. We are the ones who decide how the suggestion is shown, so we ca do it however we want. Here we have put a id=”hint”code> on our suggestion element, to access it easily.

I recommend using line-height:0code>, since we are introducing an element in the middle of a page that surely is already styled and the last thing we want is to break the beautiful looks of our web.

This is how my styling looks like, I’ve added some simple styles inline in the suggestion element.

var html = `<p style="cursor: pointer; line-height:0;" class="small p-2 pb-0 m-0">
              <span class="btn-link">
                ${suggestion.full}
              </span> ?
           </p>`;

And that would be it! Now we have an email input field that corrects our users.

Find the full code for this project in our Github.

See you guys soon ✌️

Leave a Reply

Your email address will not be published.