Feedback form with input mask. Masking and validating the phone field using jQuery. Plugin connection example

This jQuery plugin allows you to automatically select the appropriate input mask based on the entered beginning of the phone number. This allows you to make entering a phone number on a website page faster and more error-free. In addition, the developed plugin can be used in other areas if the input rules can be represented in the form of several input masks.

Introduction Web sites require entering phone number information. It so happens that each country has the right to establish its own dialing rules and number length, as a result of which confusion periodically arises between residents of different countries: some are accustomed to indicating the number with a leading digit 8, others with a leading digit 0, and still others with a + sign. Review existing solutions In order to somehow resolve the complexity and bring the numbers to a unified format, there are 3 main solutions:
  • The user is prompted to enter the number using an input mask. Advantage: The clear display of the number minimizes possible errors in the number. Disadvantage: each country has its own spelling and number length.
  • The user is asked to select the country separately and enter the rest of the number separately; possible using an input mask. Advantage: the ability to use different input masks for different countries (as well as regions within a country). Disadvantages: the list of countries (and regions within each country) can be large; the phone number ceases to exist as a single whole (or preprocessing is required before saving and displaying the number).
  • Place a + sign in front of the number (outside of input) and allow only numbers to be entered. Advantages: ease of implementation. Disadvantage: lack of visual display of the number.
  • Proposed solution As a result, it was decided to modify the usual input mask so that it changes in accordance with the current value of the number. In addition, as you enter the number, you are prompted to display the name of the identified country. This approach, subjectively, should solve all the shortcomings of the solutions listed above.

    Taking into account the fact that the number of countries in the world is relatively small, it was decided to compile a list of input masks for all countries. Information published on the website of the International Telecommunication Union was used as a source.

    The collection of this information brought many surprises. In the process of collecting information, it was necessary to take into account all possible options for telephone numbers, including within the country. However, due to the large amount of manually processed information, it is possible that inaccuracies remained in the collected database. It is planned to make corrections to the original set over time.

    Software implementation The jquery.inputmask implementation, which was mentioned many times on Habrahabr, was used as the core of the input mask. This plugin is currently being actively developed and, moreover, is designed in such a way that it is quite easy to write extensions for it. However, in this problem it turned out to be almost impossible to write such an extension. I did not modify or rewrite the original plugin to suit my needs, because... its author continues to actively work on expanding the functionality, as a result of which the application of my edits may be problematic. Therefore, I had to write a plug-in add-on over the main core, which monitors (plus intercepts) external influences and makes data modifications. In order to implement our own handlers for external influences before the handlers of the main plugin, we used the jquery.bind-first library plugin. Sorting allowed input masks To correctly select the most suitable input mask, the entire set of masks must first be sorted in a special way. When developing sorting rules, the following conventions were adopted:
  • All characters in the input mask are divided into 2 types: significant characters (in my case, this is the # symbol, meaning an arbitrary number, and numbers 0-9) and decorative characters (all others).
  • Another division of characters in the input mask is wildcard characters (in my case this is the # symbol) and all others.
  • The result is the following sorting rules in the order in which they are applied:

  • When comparing 2 input masks character-by-character, only significant characters (not decorators) are taken into account.
  • Different wildcard characters are treated as equal, and other significant characters are compared based on their code.
  • Non-template symbols are always smaller than template symbols and, as a result, are positioned higher.
  • The shorter the length of significant characters in the input mask, the smaller the input mask is considered and is positioned higher.
  • Finding a suitable input mask When comparing the input text with the next mask from the sorted list, only the significant characters of each mask are taken into account. If a line is longer than an input mask even though all preceding characters have passed the test, the input mask is considered invalid. If the input text matches several input masks, the first one is returned. Next, in the found mask, all significant characters (including non-template ones) are replaced with a template one, which is a combination of all characters allowed by any of the template symbols. Processing and interception of events In order to prevent conflicts with event handlers of the main kernel of the input mask, the following events are intercepted:
    • keydown - backspace and delete keystrokes are monitored to change the current input mask before the main handler removes one character from the text. In addition, pressing the Insert key, which changes the text input mode, is monitored for synchronization.
    • keypress - since the entered character may not be allowed by the original input mask (since all significant characters in it are replaced by a wildcard), it is necessary to check the new line to satisfy one of the allowed masks. If there are no such masks, then the character input is discarded; otherwise, the input mask is updated, after which the event is passed to the kernel handler.
    • paste , input - paste text from the clipboard. Before transferring processing to the kernel, an input mask is selected for the string resulting from pasting text from the clipboard. If the input mask cannot be found, the text is trimmed character-by-character from the end until the text matches at least one input mask. A similar operation is performed when correcting text in an input field by calling the val() function, as well as when initializing an input mask if it is applied to a non-empty input field.
    • dragdrop , drop - processing is similar to the paste event.
    • blur - additional processing in case the text clearing mode is enabled when focus is lost if it does not satisfy the input mask. This event is intercepted after the main handler, unlike the previous ones.

    All events are hung in the inputmask space. This avoids incorrect behavior when calling inputmask after the add-in is initialized (since the kernel removes all previously installed handlers in the inputmask space upon initialization).

    Usage example Mask list format A mask list is a JavaScript array of objects, preferably with the same set of properties. At least one property that contains an input mask must be present for all array objects. The name of the parameter containing the mask can be arbitrary. Below is a fragment of such an array:
    [ … ( "mask": "+7(###)###-##-##", "cc": "RU", "name_en": "Russia", "desc_en": "", " name_ru": "Russia", "desc_ru": "" ), ( "mask": "+250(###)###-###", "cc": "RW", "name_en": " Rwanda", "desc_en": "", "name_ru": "Rwanda", "desc_ru": "" ), ( "mask": "+966-5-####-####", "cc ": "SA", "name_en": "Saudi Arabia", "desc_en": "mobile", "name_ru": "Saudi Arabia", "desc_ru": "mobile" ), ( "mask": "+966- #-###-####", "cc": "SA", "name_en": "Saudi Arabia", "desc_en": "", "name_ru": "Saudi Arabia", "desc_ru": " " ), … ] Plugin connection parameters Before connecting, you need to load and sort the list of masks. This is done by executing the following function:
    $.masksSort = function(maskList, defs, match, key)
    • maskList - an array of objects storing input masks (object fragment see above);
    • defs - an array of wildcard characters (in my case it is the # symbol);
    • match - a regular expression describing significant characters (in my case this is /|#/);
    • key is the name of the array object parameter containing the input mask.

    When connected, the plugin is given a special object that describes its operation. This object contains the following set of parameters:

    • inputmask - an object containing parameters passed to the main inputmask plugin;
    • match - a regular expression describing significant characters, excluding wildcards;
    • replace - a wildcard character to which all significant characters will be replaced; may not be present in the definitions object of the inputmask object;
    • list - an array of objects describing input masks;
    • listKey - name of the parameter inside the object storing the input mask;
    • onMaskChange - a function that is called when the input mask is updated; the first parameter is an object from the array, the input mask of which corresponds to the entered text, and the second parameter is the accuracy of the mask definition: true - the input mask matches completely, false - additional characters must be entered to reliably define the mask.

    To initialize the plugin, you need to apply the inputmasks method to the input field:
    $.fn.inputmasks = function(maskOpts, mode)

    • maskOpts - an object describing the operation of the plugin;
    • mode - optional; currently the isCompleted value is supported - as a result, the method returns true if the text corresponding to the matching mask is entered completely and false otherwise.
    Plugin connection example
    Input mask var maskList = $.masksSort($.masksLoad("phone-codes.json"), ["#"], /|#/, "mask"); var maskOpts = ( inputmask: ( definitions: ( "#": ( validator: "", cardinality: 1 ) ), //clearIncomplete: true, showMaskOnHover: false, autoUnmask: true ), match: //, replace: "# ", list: maskList, listKey: "mask", onMaskChange: function(maskObj, completed) ( if (completed) ( var hint = maskObj.name_ru; if (maskObj.desc_ru && maskObj.desc_ru != "") ( hint + = " (" + maskObj.desc_ru + ")"; ) $("#descr").html(hint); ) else ( $("#descr").html("Input mask"); ) $(this ).attr("placeholder", $(this).inputmask("getemptymask")); ) ); $("#phone_mask").change(function() ( if ($("#phone_mask").is(":checked")) ( $("#customer_phone").inputmasks(maskOpts); ) else ( $ ("#customer_phone").inputmask("+[####################]", maskOpts.inputmask) .attr("placeholder", $("# customer_phone").inputmask("getemptymask")); $("#descr").html("Input mask"); ) )); $("#phone_mask").change(); Demonstration An example of a demonstration of the developed plugin is presented at

    At first glance, the answer is obvious: mark the “phone number” field as required. But there are niches in which a user can leave the site due to unnecessary required fields. For example, applications, software, sites for selling content. However, user numbers for such projects are important as additional data that makes it possible to interact with potential clients in the future. Moreover, there is a simple and effective solution - using an input mask. Let's prove this with cases.

    Why do you need an input mask?

    The input mask shows in what format you need to enter data in the field. For example, if a user enters a phone number without an area code or a phone number in the address field, they will not be able to advance to the next item. The mask in forms provides a uniform appearance of the entered data, which simplifies the search and management of the database. According to Netpeak recommendations, a telephone number input mask is a required element of the order form on the website. Like any practical recommendation from our specialists, this provision is based on successful client cases.

    Since the site specialized in applications, the field for entering a number on the site was optional. To track changes in the percentage of customers filling out the “phone” field on the order form on the site, we used a custom variable in Google Analytics. In February, none of the nine customers filled out the “phone” field. In March, we introduced an input mask, and users began filling it out. For the purity of the experiment, the field was left optional and no other changes were made.
    The result for March was 19 issues with 22 users filling out the application. In other words, 85% of users who ordered the application left their phone number.

    Information center website case study: increase in the number of phone number field entries by 15.4%

    The client’s website provides services for writing essays, coursework, dissertations and other works. The user's phone number is desirable for communication, but on the site this field was optional. The user could not fill out the form or write anything in this field. As the first time, at the first stage we set up tracking for filling out the “phone” field in Google Analytics. In November, out of 59 applications left by site visitors, 15 did not contain numbers. That is, the company received only 74.6% of submitted forms with a completed number. We then added an input mask to the phone field. In December, the site received 60 applications. Moreover, only 6 completed forms did not contain the client’s phone number. Consequently, 90% of the order forms submitted contained a correctly filled in telephone field. Over the month - an increase of 15.4%, only thanks to the introduction of a number entry mask. Finally, an internal case study of the agency.

    The field for numbers in the order form on the Netpeak website is also optional. But phone numbers are important so that clients always know the number and status of their application (we send this data via SMS), as well as to optimize the work of account managers. The experiment period, as in previous examples, is two months. As a result of the appearance of the number input mask, the percentage of forms filled out increased from 44% to 83% - by 39.4%.
    After the experiment, we implemented the mask on the site. The field for entering a number remains optional. The application will not be processed only if an incorrect number is entered in this field. In this case, the first digits in the mask change depending on the country in which the site user is located. If we are talking about a pre-order form, then it is interesting to know how much better quality applications with phone numbers are. We took all the leads ever received through the pre-order form and calculated the percentage of those that converted into customers without and with a number. The latter turned out to be the most by 0.81%.

    Conclusion: if a person leaves you his number on the form, this does not guarantee the transfer of funds.

    If we talk about the pre-order form, the possibilities for reactivating customers come to the fore, opportunities that open up for marketers armed with a database of users’ personal phone numbers. We will talk about successful reactivation cases in one of our new posts. In the meantime, we advise you to read about the measures that should be taken before any actions with telephone numbers in the database. Share in the comments your experience of implementing an input mask for phone numbers and your observations on the quality of incoming applications.

    This jQuery plugin allows you to automatically select the appropriate input mask based on the entered beginning of the phone number. This allows you to make entering a phone number on a website page faster and more error-free. In addition, the developed plugin can be used in other areas if the input rules can be represented in the form of several input masks.

    Introduction Web sites require entering phone number information. It so happens that each country has the right to establish its own dialing rules and number length, as a result of which confusion periodically arises between residents of different countries: some are accustomed to indicating the number with a leading digit 8, others with a leading digit 0, and still others with a + sign. Review existing solutions In order to somehow resolve the complexity and bring the numbers to a unified format, there are 3 main solutions:
  • The user is prompted to enter the number using an input mask. Advantage: The clear display of the number minimizes possible errors in the number. Disadvantage: each country has its own spelling and number length.
  • The user is asked to select the country separately and enter the rest of the number separately; possible using an input mask. Advantage: the ability to use different input masks for different countries (as well as regions within a country). Disadvantages: the list of countries (and regions within each country) can be large; the phone number ceases to exist as a single whole (or preprocessing is required before saving and displaying the number).
  • Place a + sign in front of the number (outside of input) and allow only numbers to be entered. Advantages: ease of implementation. Disadvantage: lack of visual display of the number.
  • Proposed solution As a result, it was decided to modify the usual input mask so that it changes in accordance with the current value of the number. In addition, as you enter the number, you are prompted to display the name of the identified country. This approach, subjectively, should solve all the shortcomings of the solutions listed above.

    Taking into account the fact that the number of countries in the world is relatively small, it was decided to compile a list of input masks for all countries. Information published on the website of the International Telecommunication Union was used as a source.

    The collection of this information brought many surprises. In the process of collecting information, it was necessary to take into account all possible options for telephone numbers, including within the country. However, due to the large amount of manually processed information, it is possible that inaccuracies remained in the collected database. It is planned to make corrections to the original set over time.

    Software implementation The jquery.inputmask implementation, which was mentioned many times on Habrahabr, was used as the core of the input mask. This plugin is currently being actively developed and, moreover, is designed in such a way that it is quite easy to write extensions for it. However, in this problem it turned out to be almost impossible to write such an extension. I did not modify or rewrite the original plugin to suit my needs, because... its author continues to actively work on expanding the functionality, as a result of which the application of my edits may be problematic. Therefore, I had to write a plug-in add-on over the main core, which monitors (plus intercepts) external influences and makes data modifications. In order to implement our own handlers for external influences before the handlers of the main plugin, we used the jquery.bind-first library plugin. Sorting allowed input masks To correctly select the most suitable input mask, the entire set of masks must first be sorted in a special way. When developing sorting rules, the following conventions were adopted:
  • All characters in the input mask are divided into 2 types: significant characters (in my case, this is the # symbol, meaning an arbitrary number, and numbers 0-9) and decorative characters (all others).
  • Another division of characters in the input mask is wildcard characters (in my case this is the # symbol) and all others.
  • The result is the following sorting rules in the order in which they are applied:

  • When comparing 2 input masks character-by-character, only significant characters (not decorators) are taken into account.
  • Different wildcard characters are treated as equal, and other significant characters are compared based on their code.
  • Non-template symbols are always smaller than template symbols and, as a result, are positioned higher.
  • The shorter the length of significant characters in the input mask, the smaller the input mask is considered and is positioned higher.
  • Finding a suitable input mask When comparing the input text with the next mask from the sorted list, only the significant characters of each mask are taken into account. If a line is longer than an input mask even though all preceding characters have passed the test, the input mask is considered invalid. If the input text matches several input masks, the first one is returned. Next, in the found mask, all significant characters (including non-template ones) are replaced with a template one, which is a combination of all characters allowed by any of the template symbols. Processing and interception of events In order to prevent conflicts with event handlers of the main kernel of the input mask, the following events are intercepted:
    • keydown - backspace and delete keystrokes are monitored to change the current input mask before the main handler removes one character from the text. In addition, pressing the Insert key, which changes the text input mode, is monitored for synchronization.
    • keypress - since the entered character may not be allowed by the original input mask (since all significant characters in it are replaced by a wildcard), it is necessary to check the new line to satisfy one of the allowed masks. If there are no such masks, then the character input is discarded; otherwise, the input mask is updated, after which the event is passed to the kernel handler.
    • paste , input - paste text from the clipboard. Before transferring processing to the kernel, an input mask is selected for the string resulting from pasting text from the clipboard. If the input mask cannot be found, the text is trimmed character-by-character from the end until the text matches at least one input mask. A similar operation is performed when correcting text in an input field by calling the val() function, as well as when initializing an input mask if it is applied to a non-empty input field.
    • dragdrop , drop - processing is similar to the paste event.
    • blur - additional processing in case the text clearing mode is enabled when focus is lost if it does not satisfy the input mask. This event is intercepted after the main handler, unlike the previous ones.

    All events are hung in the inputmask space. This avoids incorrect behavior when calling inputmask after the add-in is initialized (since the kernel removes all previously installed handlers in the inputmask space upon initialization).

    Usage example Mask list format A mask list is a JavaScript array of objects, preferably with the same set of properties. At least one property that contains an input mask must be present for all array objects. The name of the parameter containing the mask can be arbitrary. Below is a fragment of such an array:
    [ … ( "mask": "+7(###)###-##-##", "cc": "RU", "name_en": "Russia", "desc_en": "", " name_ru": "Russia", "desc_ru": "" ), ( "mask": "+250(###)###-###", "cc": "RW", "name_en": " Rwanda", "desc_en": "", "name_ru": "Rwanda", "desc_ru": "" ), ( "mask": "+966-5-####-####", "cc ": "SA", "name_en": "Saudi Arabia", "desc_en": "mobile", "name_ru": "Saudi Arabia", "desc_ru": "mobile" ), ( "mask": "+966- #-###-####", "cc": "SA", "name_en": "Saudi Arabia", "desc_en": "", "name_ru": "Saudi Arabia", "desc_ru": " " ), … ] Plugin connection parameters Before connecting, you need to load and sort the list of masks. This is done by executing the following function:
    $.masksSort = function(maskList, defs, match, key)
    • maskList - an array of objects storing input masks (object fragment see above);
    • defs - an array of wildcard characters (in my case it is the # symbol);
    • match - a regular expression describing significant characters (in my case this is /|#/);
    • key is the name of the array object parameter containing the input mask.

    When connected, the plugin is given a special object that describes its operation. This object contains the following set of parameters:

    • inputmask - an object containing parameters passed to the main inputmask plugin;
    • match - a regular expression describing significant characters, excluding wildcards;
    • replace - a wildcard character to which all significant characters will be replaced; may not be present in the definitions object of the inputmask object;
    • list - an array of objects describing input masks;
    • listKey - name of the parameter inside the object storing the input mask;
    • onMaskChange - a function that is called when the input mask is updated; the first parameter is an object from the array, the input mask of which corresponds to the entered text, and the second parameter is the accuracy of the mask definition: true - the input mask matches completely, false - additional characters must be entered to reliably define the mask.

    To initialize the plugin, you need to apply the inputmasks method to the input field:
    $.fn.inputmasks = function(maskOpts, mode)

    • maskOpts - an object describing the operation of the plugin;
    • mode - optional; currently the isCompleted value is supported - as a result, the method returns true if the text corresponding to the matching mask is entered completely and false otherwise.
    Plugin connection example
    Input mask var maskList = $.masksSort($.masksLoad("phone-codes.json"), ["#"], /|#/, "mask"); var maskOpts = ( inputmask: ( definitions: ( "#": ( validator: "", cardinality: 1 ) ), //clearIncomplete: true, showMaskOnHover: false, autoUnmask: true ), match: //, replace: "# ", list: maskList, listKey: "mask", onMaskChange: function(maskObj, completed) ( if (completed) ( var hint = maskObj.name_ru; if (maskObj.desc_ru && maskObj.desc_ru != "") ( hint + = " (" + maskObj.desc_ru + ")"; ) $("#descr").html(hint); ) else ( $("#descr").html("Input mask"); ) $(this ).attr("placeholder", $(this).inputmask("getemptymask")); ) ); $("#phone_mask").change(function() ( if ($("#phone_mask").is(":checked")) ( $("#customer_phone").inputmasks(maskOpts); ) else ( $ ("#customer_phone").inputmask("+[####################]", maskOpts.inputmask) .attr("placeholder", $("# customer_phone").inputmask("getemptymask")); $("#descr").html("Input mask"); ) )); $("#phone_mask").change(); Demonstration An example of a demonstration of the developed plugin is presented at