📜 ⬆️ ⬇️

Formatting prices, or how I input rewrote

At work, I recently encountered a seemingly trivial task - formatting the price and dividing it by grade.
Nothing complicated, I decided. Especially on the Internet, there are already a lot of ready-made solutions from simple and boring (we expand the line, we add spaces every 3 characters and we expand back) to quite interesting ones (I am sure that many have seen this regular schedule, but this is not about it)
price.replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1 ') 


Looking ahead - this is not a story about how I tried using one of the standard methods to accomplish a task, or how I cracked crutches.
Before I started, I studied a lot of materials and the floor of hundreds of libraries. I did not find such a functional anywhere.
I hope it will be useful to someone.

I even came across the library, to break numbers by digits, but I decided to stop at the aforementioned regular schedule.
Hung up formatting on keyup , what could be more difficult?

The first thing testers didn't like is entering letters. as the event hangs on the keyup, until the key is released the letter appears for a split second. If you hold it, then we have in this field a string of letters that disappears by the release of a key.
Not a problem, I thought and hung up on keydown
 var code = event.keyCode; if((code < 48 || code > 57) && (code < 96 || code > 105)) { event.preventDefault(); return; } 

the first condition for the numeric keys on top, and the second for the NumPad
great, now if you press anything other than numbers, nothing will happen.
Last week, I forgot to think about this small formatted input, when suddenly a list of errors on it fell on me.
To vskidku -

And a lot of things associated with clicking on buttons
not such a big problem I thought and added to the keyup
 if ( code == 9 || // tab code == 27 || // ecs event.ctrlKey === true || //     ctrl event.metaKey === true || event.altKey === true || //     alt event.shiftKey === true || //     shift (code >= 112 && code <= 123) || // F1 - F12 (code >= 35 && code <= 39)) // end, home,  { return; } 

To track the cursor positions made 2 functions get / set-CursorPosition
and for each keyup
  var cursor = $(this).getCursorPosition(); $(this).val(priceFormatted(value)); $(this).setCursorPosition(cursor); 

')
I was busy testing all this code and realized that I could not catch the keyup event when I pressed double keys - for example, Ctrl + A.
In theory, the whole text should stand out, but in fact the following happened. nothing happened on the keydown ( event.ctrlKey === true; return false ) and the text was highlighted. By keyup, the text was reformatted and the selection was reset.
In the beginning, I tried to fudge something with checking the previous length of the value and the new one, but when you need to delete characters (select and press a letter / number) everything refused to work.
As a result, it was decided to abandon the keyup completely, and go completely to the keydown .
This did not bode well, because I very much doubted the cross-browser compatibility of this solution, and in general I didn’t really want to read the codes of each key and add where I needed the characters myself.

Generally what came out of all this.

First of all, let's designate those variables that will be useful in the future in any case
  var cursor = $(this).getCursorPosition(); var code = event.keyCode; var startValue = $(this).val(); 


First you need to determine what key was pressed
  if ((code >= 48 && code <= 57)) { key = (code - 48); } else if ((code >= 96 && code <= 105 )) { key = (code - 96); } else { return false; } 

The keys with code 48 - 57 are the upper digits 0 - 9, and the code 96 - 105 corresponds to numpadovskim
If another key is pressed then we do nothing.
In the place where the cursor was, insert the new value, format and rearrange the cursor.
  var value = startValue.substr(0, cursor) + key + startValue.substring(cursor, startValue.length); $(this).val(priceFormatted(value)); $(this).setCursorPosition(cursor + $(this).val().length - startValue.length); 


Not bad, but what will happen if you select some text and try to write a number? Correctly, the text will not be removed and the new number will replace the old one.
Each time you click to remove the selected text is not difficult - jquery plugin
 $(this).delSelected(); 


Now back to the backspace and delete keys. It's all quite simple too
 $(this).val(startValue.substr(0, cursor - 1) + startValue.substring(cursor, startValue.length)); //   //  $(this).val(startValue.substr(0, cursor) + startValue.substring(cursor + 1, startValue.length)); //   

Accordingly, adding a check on the selection, because if you select the text and press backspace or delete, then nothing except the selected will be deleted.
Also needed was the logic of the work if the cursor is before the space and the user presses backspace
After all the manipulations, clicking on the backspase looked like this.
  var delCount = $(this).delSelected(); if (!delCount) { if (startValue[cursor - 1] === ' ') { cursor--; } $(this).val(startValue.substr(0, cursor - 1) + startValue.substring(cursor, startValue.length)); } $(this).val(priceFormatted($(this).val())); $(this).setCursorPosition(cursor - (startValue.length - $(this).val().length - delCount)); 


Clicking on delete looked almost the same, only most of the addition / subtraction signs are reversed.
In the evening, the task came back to me with new reports.


Need to do. The interdiction of insertion implementation succumbed very easily
 if ( (event.metaKey === true && code == 86) || (event.ctrlKey === true && code == 86) || // Ctrl+V | Shift+insert (event.shiftKey === true && code == 45) ) { return false; } 

And the ban on opening the context menu
 .bind('contextmenu', function (event) { event.preventDefault(); }) 


Dragging also did not foreshadow any particular problems.
 .bind('drop', function (event) { // ... 


And here began interesting things in, oddly enough, chrome.
He alone refused to process correctly and if I did a function
 event.preventDefault(); //  return false; 

He left the second cursor in the input, which was not deleted by any means except refreshing the page or the console
 $('...').val(''); //   

The problem solved an extremely ugly piece of code
 .bind('drop', function (event) { var value = $(this).val(); $(this).val(''); //    //       . //         . $(this).val(value); event.preventDefault(); }) 


If anyone faced this problem and decided to write it off please.

This ended my adventures with price formatting.
While 2 weeks is a normal flight, no bugs are noticed, the cross-browser compatibility is not broken.

Now I decided to share with everyone, because I did not find any analogs on the Internet. Designed all in the form of jquery library

Poke and click can be here (jsfiddle)
and download - here (github)

Source: https://habr.com/ru/post/229007/


All Articles