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 the phone number on the 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 input of phone number information. It so happened that each country has the right to set its own rules for dialing and the length of the number, resulting in confusion between residents of different countries: some are used to indicate the number with the leading number
8
, others with the leading number
0
, and still others with the
+
sign.
Review of existing solutions
In order to somehow resolve the arisen complexity and bring the numbers to a single format, there are 3 main solutions:
- The user is prompted to enter the number using the input mask. Advantage: the visual display of the number minimizes possible errors in the room. Disadvantage: each country has its own spelling and number length.
- The user is prompted to select a country separately and separately enter the rest of the number; possibly using an input mask. Advantage: the ability to use different input masks for different countries (as well as regions within the country). Disadvantages: the list of countries (and regions within each country) can be large; the phone number ceases to exist as a whole (or preprocessing is required before storing and displaying the number).
- Put the
+
sign in front of the number (outside input) and allow only the input of numbers. Advantages: ease of implementation. Disadvantage: no visual display of the number.
Proposed Solution
As a result, it was decided to modify the usual input mask so that it changed in accordance with the current value of the number. In addition, as you enter the number, it is proposed to display the name of the determined country. This approach, subjectively, should solve all the shortcomings of the above solutions.
')
Given that the number of countries in the world is relatively small, it was decided to compile a list of input masks for all countries. As a source,
information published on the website of the International Telecommunication Union was used.
The collection of this information gave a lot of surprises. In the process of collecting information, it was necessary to take into account all possible variants of telephone numbers, including domestically. However, due to the large amount of manually processed information, it is possible that there were inaccuracies in the collected database. Over time, it is planned to make corrections to the initial set.
Software implementation
As the
of the input mask, the
jquery.inputmask implementation was used, which was repeatedly mentioned in Habrahabr. This plugin is now actively developing and, moreover, it is designed in such a way that it is enough for it to simply write extensions. However, in this task, it was almost impossible to write such an extension. I did not finish or rewrite the original plugin to fit my needs, because its author continues to actively work on expanding the functionality, as a result of which the application of my edits can be problematic. So I had to write a plugin add-on over the main core, which monitors (plus intercepts) external influences and modifies the data. In order to implement their external impact handlers before the main plug-in
handlers, the jquery.bind-first plugin library was
used .
Sorting allowed input masks
To correctly select the most appropriate input mask, the entire set of masks must be pre-sorted in a special way. In developing the rules for sorting, the following conventions were adopted:
- All the characters in the input mask are divided into 2 types: meaningful characters (in my case it is the
#
symbol, meaning an arbitrary digit, and numbers 0-9) and decorator symbols (all the rest). - Another division of characters in the input mask is template characters (in my case it is the
#
character) and all the 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 are taken into account (not decorators).
- Different wildcard characters are perceived as equal, and other significant characters are compared based on their code.
- Non-generic characters are always less patterned and as a result are placed higher.
- The shorter the length of significant characters in the input mask, the smaller the input mask is and is higher.
Search for a suitable input mask
When comparing input text with a regular mask from a sorted list, only the significant characters of each mask are taken into account. If the line is longer than the input mask, despite the fact that all previous characters have passed the test, this input mask is considered unsuitable. If several input masks satisfy the input text, the first one is returned. Then, in the found mask, all significant characters (including non-template ones) are replaced with wildcard characters, which is a combination of all the characters allowed by any of the wildcard characters.
Event Handling and Interception
In order to prevent conflicts with the event handlers of the main kernel of the input mask, the following events are intercepted:
- keydown — tracks Backspace and Delete are tracked — in order to change the current input mask before the main processor removes one character from the text. In addition, it tracks the keystroke Insert, which changes the text input mode for synchronization.
- keypress - since the input character may not be allowed by the original input mask (since all significant characters in it are replaced with wildcard characters), you need to check the new line for the satisfaction of one of the allowed masks. If there are no such masks, 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 line resulting from pasting text from the clipboard. In case the input mask could not be picked up, a character reduction of the text from the end is performed — until the text satisfies at least one input mask. A similar operation is performed when correcting text in the input field by calling the function val (), as well as when initializing the 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 turned on when the 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
in the inputmask space. This allows you to avoid incorrect behavior when calling inputmask after the add-in is initialized (since the kernel, during initialization, removes all previously installed handlers in the inputmask space).
Usage example
The format of the list of masks
The list of masks 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 objects in the array. 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": "", "desc_ru": "" }, { "mask": "+250(###)###-###", "cc": "RW", "name_en": "Rwanda", "desc_en": "", "name_ru": "", "desc_ru": "" }, { "mask": "+966-5-####-####", "cc": "SA", "name_en": "Saudi Arabia ", "desc_en": "mobile", "name_ru": " ", "desc_ru": "" }, { "mask": "+966-#-###-####", "cc": "SA", "name_en": "Saudi Arabia", "desc_en": "", "name_ru": " ", "desc_ru": "" }, … ]
Plugin connection options
Before connecting you need to download and sort the list of masks. This is done by performing the following function:
$.masksSort = function(maskList, defs, match, key)
- maskList - an array of objects that contain input masks (an object fragment, see above);
- defs - an array of wildcard characters (in my case it is the
#
character); - match is a regular expression that describes significant characters (in my case, this is
/[0-9]|#/
); - key - the name of the parameter of the array object containing the input mask.
When connected, the plug-in is passed 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 is a regular expression that describes significant characters, with the exception of wildcards;
- replace is a wildcard character to which all significant characters will be replaced; may be missing in the inputmask definitions object;
- list - an array of objects describing input masks;
- listKey - the name of the parameter inside the object that stores the input mask;
- onMaskChange is a function that is called when the input mask is updated; As the first parameter, an object is transferred from the array, the input mask of which corresponds to the entered text, and the second parameter is the mask definition accuracy: true - the input mask fully matches, false - for reliable mask definition, additional characters are required.
To initialize the plugin, you need to apply the inputmasks method to the input field:
$.fn.inputmasks = function(maskOpts, mode)
- maskOpts - an object that describes the operation of the plugin;
- mode is optional; the
isCompleted
value is currently 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 type="text" id="customer_phone" value="7" size="25"><br> <input type="checkbox" id="phone_mask" checked> <label id="descr" for="phone_mask"> </label> <script> var maskList = $.masksSort($.masksLoad("phone-codes.json"), ['#'], /[0-9]|#/, "mask"); var maskOpts = { inputmask: { definitions: { '#': { validator: "[0-9]", cardinality: 1 } }, //clearIncomplete: true, showMaskOnHover: false, autoUnmask: true }, match: /[0-9]/, 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(" "); } $(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(" "); } }); $('#phone_mask').change(); </script>
Demonstration
An example of a demonstration of the developed plugin is presented on
the project page .