Why the fake keyboard?

Or input box, cursor all false false keyboard?

Mobile phone with don’t have to write a fake, full of nothing to do?

Pack to force? Show off?

The baby is also forced, the baby is also very grievance

Background to the problem

Mobile TERMINAL H5 project requirements:

Into a page automatic popup with decimal digital keyboard, and bring their own input validation, amount, for example — can only input Numbers and the decimal point, and can only enter a decimal, small digital no more than two, and before the input validation is to not let illegal input, (UE extra functions — > custom cursor color. < it is against humanity needs). The breakdown is as follows:

  • The related page is displayed, and the input box automatically obtains the focus
  • Automatic keyboard eject
  • A numeric keypad with a decimal point pops up
  • Numeric input is automatically verified. Only one decimal point can be entered, and the number of decimal places cannot exceed 2. If the number exceeds 2, the input cannot continue
  • If the cursor is in the first digit and you type a ‘.’, then a ‘0’ is put in and a ‘.’ is inserted.

Implementation programming

1. Implementation scheme based on input + mobile phone built-in keyboard

(1) For function point 1, you can set the property autofocus for input, and the input box can be automatically focused. The easier

(2) For function point 2, setting the input property autoFocus will automatically focus but the keyboard will not automatically pop up;

Must manually click the input box keyboard will pop up; Therefore, when entering the page, I used JS to trigger click or foucus, and found that the keyboard would not automatically pop up, and delayed click and focus could not pop up either. There is only one last option — let the NA end provide a way for the keyboard to pop up. Pure front end can not be fixed, need NA end assistance /, or find PM to cut down the automatic keyboard requirements >.< (barely acceptable)

(3) Set type = “number” or type = “tel”; The former has a numeric keypad on Android and only a full keyboard on ios, while the latter has a numeric keypad on both Android and ios, but!! Pit dad, the number keyboard did not pop up decimal point! (My Huawei Glory 9 is very good to give me a number keyboard with decimal point, it is not easy to ah) can only choose type = “number”, just can accept ios play full keyboard

(4) For function point 4, set type = “number”, found that can keep input decimal point ah ah ah ah looks really crazy, the first time input decimal point does not automatically become ‘0 ‘.

At this point, you must think of using the event to listen for the typed character, before the input judgment, and then decide whether to enter the input box.

You’ll be happy to think of a bunch of events that might be useful: OnKeyDown, onKeyUp, onChange, onInput, onPropertyChange, textInput.

There is still a long way to go. I have tried and tried and found many problems.

  • Onkeyup — it is triggered every time a character is added or deleted, but when a character is added, it is triggered only after the value is entered.
  • Onchange — is triggered when the content changes (the two may be equal) and the focus is lost, and there is no pre-input validation.
  • Onpropertychange — The onChange event is fired when the content changes (it is possible that the content is equal twice) and loses focus; This event is triggered every time a character is added or removed. This event is also triggered by js changes, but this event is exclusive to IE.
  • Oninput – Mobile not supported on many phones.

(Only onkeyup/textInput left, there is still a glimmer of hope just ballet >.<.)

  • Onkeyup — Its event has two related properties event.key and event.keycode. Event. key didn’t work on my Huawei Honor 9 (and predictably on other older phones). But it also has a property called event.keycode whose value on the PC side is the ASCII code for the typed character. However, the value of any number or decimal point entered on the mobile terminal is 229 (Huawei Glory 9 test), so OnKeyUp will not work.

  • OntextInput — supported on PC and mobile!! Its event.data gets the input value. Jubilant, celebrating the whole country, ah ha ha ~~

Finally a sigh of relief, as long as you can get the value before entering it, you can verify it.

Write the verification process in one go with confidence:

html

<input
    id="amount-input"
    autofocus
    type="number"
    @textInput="checkNumber"
    v-model="amount"
    require/> 
Copy the code

js

checkNumber(event) {
  var key = event.data || ' ';
  if (key.search(/[0-9\.]/) > -1) {
     var value = document.getElementById('amount-input').value;
     if (key === '. ' && value.search(/\./) > -1) {
        event.preventDefault();
     }
     if(value.search(/\.\d{2}/) > -1) { event.preventDefault(); }}else{ event.preventDefault(); }},Copy the code

It happened again ~~~~~ the effect I expected was still not achieved.

Description Failed to obtain all characters in the input box using value

Input type = number input type = number input type = number

That is, if you enter ’12.’ and you get ’12’ from value, if you enter ‘.’ only, you get an empty string ‘ ‘from value, and you don’t get the decimal point. So you can’t tell if you can input a decimal point, so you can’t tell if you can still input a decimal point, so you can still input an infinite number of decimal points, and the problem is still not solved.

Try:

  • Use the two-way binding this.amount in VUE to get all the characters in the input, and find that the value obtained by this.amount is the same as the value obtained by value. The attempt failed.
  • We maintain our own character array for the input values obtained through textInput. But textInput does not trigger deletion, so it cannot get all the exact characters in the input box in real time. And because you can’t get the exact position of the cursor in the input input box to determine which character to delete, the character array cannot be maintained accurately. The attempt failed.

(5) For function point 5, function 4 solved, function 5 is a small case…

Therefore, it is difficult to achieve the implementation scheme based on input + mobile phone built-in keyboard to meet the above requirements

2. Based on input + false digital keyboard implementation scheme

If you use a fake keyboard with a native input field, you need to:

  • Disable the mobile phone’s built-in keyboard
  • Gets the contents of the Input Input box

To disable the built-in keyboard, you can set the readonly attribute of Input in the absence of NA exposed method support. In this case, the input box can not add or delete characters. If the NA terminal can provide the premise of disabling the mobile phone built-in keyboard method, to achieve click on the false keyboard input box can add and delete characters.

If you want to add or delete the value from the Input type=text, it’s easy to do. You just need to concatenate the corresponding character of the keyboard to the value obtained from the Input type=text. But if the cursor isn’t at the end, it’s in the middle

Figure 2 example of cursor between numbersCopy the code

So when we click on the fake keyboard to add or remove a character, how can we know where to add or remove a character. You may need to get the cursor position. Currently only IE and Firefox support document.selection, selectionStart can get the cursor position.

// Get the cursor positionfunction getCursortPosition (textDom) {
 var cursorPos = 0;
 if (document.selection) {
  // IE Support
  textDom.focus ();
  var selectRange = document.selection.createRange();
  selectRange.moveStart ('character', -textDom.value.length);
  cursorPos = selectRange.text.length;
 }else if (textDom.selectionStart || textDom.selectionStart == '0') {
  // Firefox support
  cursorPos = textDom.selectionStart;
 }
 return cursorPos;
}
Copy the code

Since our project is the development project of mobile terminal H5, considering compatibility, it is obvious that the above methods cannot be compatible with most models.

3. Input box, cursor, numeric keyboard all false implementation scheme

Neither of the above solutions is feasible, so I can only imagine that a set of equipment of false input box, false cursor and false keyboard is needed to realize the fake keyboard that meets the above requirements. So that I can control all the elements, all the above problems can be solved.

It’s easy to add and delete a fake keyboard without a cursor from the very end of the keyboard. Just bind a click event to each key, maintain an array, and maintain the contents of the input box with each push or pop from the back.

Figure 3 can only be added and removed from the last renderings without the cursorCopy the code

But it’s a terrible experience compared to a real input field.

The difficulties in

The difficulty in creating a fake keyboard that has the same experience as a native keyboard and comes with input validation is:

  • A cursor is displayed and the cursor is blinking
  • Cursor positioning, click on the middle of the number to automatically move the cursor past
  • According to the cursor position to achieve insertion and deletion
  • To lose focus and hide the cursor, click the input box to display the cursor and pop up the keyboard

Native JS implementation

For the cursor implementation, create an element to set the background color, which you can control to hide and appear.

For the “click the number in the middle of the cursor to move over”, you can add each number or decimal point to add an empty element with the click event space, and then add the character to enter. Space is to bind a click event that tells the cursor where to move.

// Insert a character in front of the cursorfunction insert(value) {
	var span = document.createElement("span"); // Create the element with the value span.classname ='val';
	span.innerText = value;

	var space = document.createElement("span");
	space.className = 'space';
	space.addEventListener('click', moveCursor);

	var cursor = document.getElementsByClassName('cursor') [0]; inputArea.insertBefore(space, cursor); Inputarea.insertbefore (span, cursor); // Insert value}Copy the code

Delete the numeric character before the cursor, and then delete the space element.

// Delete the elementfunction deleteElement() {
	setCursorFlash();
	var cursor = document.getElementsByClassName('cursor') [0]; var n = 2; // Two delete actionswhile(cursor.previousSibling && n > 0) {
    inputArea.removeChild(cursor.previousSibling );
    n--;
 	}
	if(getInputStr().search(/^\.\d*/) > -1) {
		insert(0);
	}
	if(getInputStr() === ' ') {/ / is empty placeholder to display an element var placeholder = document. The getElementsByClassName ('holder') [0]; placeHolder.className ='holder'; }}Copy the code

You can see the add and remove process through the chrome element review.

Figure 4 Changes of add, Delete, and cursor movement elementsCopy the code

Each space element is bound to a click event that moves the cursor. There is a right-space that can be placed on the right, and a click event that moves the cursor to the last bit.

// Move the cursorfunction moveCursor(event) {
	var cursor = document.getElementsByClassName('cursor') [0]; // Get the cursorif(event.currentTarget.className == 'right-space') {if(! cursor.nextSibling || cursor.nextSibling.nodeName =='#text') {return;
		} else{ var ele = cursor.nextSibling; inputArea.insertBefore(inputArea.lastElementChild, ele); inputArea.appendChild(cursor); }}else {
		var tempEle = event.currentTarget.nextSibling;
		// var nodeName = event.currentTarget.nextSibling.nodeName;
		// var cursor = document.getElementsByClassName('cursor') [0];if(! tempEle || tempEle.nodeName =='#text') { var temp = event.currentTarget.previousSibling; var ele = inputArea.replaceChild( event.currentTarget, cursor); // Replace the cursor with the current element inputarea.appendChild (ele); }else{ var temp = event.currentTarget.nextSibling; var ele = inputArea.replaceChild( event.currentTarget, cursor); // Replace the cursor with the current element inputarea.insertBefore (ele, temp); }}}Copy the code

As you can see from the GIF above, the cursor is always one and has a scheduled task. The cursor flashing Settings are as follows, using the native setInterval.

// Set the cursor scheduled taskfunction setCursorFlash() {/ / placeholder hidden var placeholder = document. The getElementsByClassName ('holder') [0]; placeHolder.className ='holder hidden';

	var cursor = document.getElementsByClassName('cursor') [0]; var inputContainer = document.getElementsByClassName('input-container') [0]; cursor.className ="cursor";
	var isShowCursor = true;
	inputContainer.focus();
	showKeyBoard();
	if (intervalId) {
		clearInterval(intervalId);
	}
	intervalId = setInterval(function() { isShowCursor = ! isShowCursor;if (isShowCursor) {
			cursor.className = 'cursor';
		} else {
			cursor.className = 'cursor hidden'; }}, 1000); }Copy the code

The final use of native JS implementation with input box, cursor, keyboard of false number keyboard.

In addition to the above functions, but also to achieve the input before the verification function, in order to follow close to the real input box performance, at the same time to achieve click

Input box to get the focus, cursor flash, pop-up keyboard; Lost focus cursor disappears.

Why not use jQuery?

One is that the current H5 project does not use jQuery.

Second, there is little need to directly manipulate the DOM after using VUE, and a few methods are implemented lighter if only for use

One or two methods and introducing jQuery will make the project heavier.

Native JS implementation effects

Figure 5 native JS input box, cursor, keyboard full fake suite renderings source github.com/DaisyWang88…

Phone code validation: esau sandbox. Runjs. Cn/show/mvjrca… (GetCrx.cn)

Because the mobile click event has a 300ms delay, the effect of the native JS implementation is a little less smooth. If you need to use the native JS implementation version, make the FastClick or Zepto tap event to solve the latency problem.

PS: Before said that “VUE itself solves the problem of 300ms delay”, after the textual research found that it was wrong, I am really sorry for the trouble.

VUE’s click events are native and do not handle this delay. To avoid any confusion, the Github demo has fixed the latency issue with fastClick (it was lazy before >.<). The native JS implementation is now smooth.

VUE componentization

Considering that some application scenarios in the project have multiple input boxes, of course, only one keyboard is needed for input, so the input box is used as a component v-input and the keyboard is used as a component V-Keyboard during componentization.

The interaction between the input box and the keyboard

The interaction diagram is as follows:

Figure 6 VUE component interaction diagramCopy the code

Considering that there are multiple input boxes on one page in this project, it is necessary to control which input box the keyboard is used with.

In order to achieve this purpose, the method of “when clicking the input box to get the focus, the current v-Input input box component instance to the V-Keyboard keyboard component” is adopted.

this.$refs.virtualKeyBoard.$emit(‘getInputVm’, this.$refs.virtualInput); As shown in Figure 6, the V-Keyboard component listens on the ‘getInputVm’ event and obtains an instance of V-Input.

After you get an instance of the v-input component, you can add or remove characters according to the keyboard click events.

In this way, even if there are multiple input boxes, it is convenient to control the operation between the keyboard and input boxes.

The input box automatically gets the focus, and the keyboard automatically pops up

Requirements to enter a page input box automatically get the focus, the keyboard automatically pops up.

  • Automatically Obtain focus You can set is-auto-focus to control whether to automatically obtain focus.
<v-input
    ref="virtualInput"
    v-model="amount"
    :placeholder="placeText"
    :is-auto-focus="true"
    @show-key-board="showKeyBoard">
</v-input>
Copy the code
  • To automatically pop up the keyboard as shown in Figure 6, you need to pass an instance of the corresponding input box component V-Input to the keyboard component V-Keyboard after the page is instantiated.
this.$refs.virtualKeyBoard.$emit('getInputVm', this.$refs.virtualInput);
Copy the code

After the ‘getInputVm’ event is captured between keyboard groups, an instance of the corresponding input box is obtained and automatically pops up.

this.$on('getInputVm'.function(obj) {
     this.refObject = obj;
     this.isShow = true;
});
Copy the code

V – model to support

Vue supports custom V-Model props for child components to set a value.

props: {
    value: {
      type: String,
      default: ' ',}}Copy the code

When the value changes, $emit an ‘input’ event and emit the corresponding value. This.getinputstr () is the function used to get the string in the input box.

this.$emit('input', this.getInputStr());
Copy the code

The effect is as follows:

See github.com/DaisyWang88… .

conclusion

The native input setting type =number makes it difficult to verify and control the number of decimal points and decimal places before input, and there are various compatibility problems in retrieving the value before input. Currently, only ontextInput can accurately retrieve the value before input. There is also a key problem when the value of type =number does not contain a decimal point, making it almost impossible to use regular validation before input. If you set type= text, you can get all the characters in the input box, but you cannot pop up the numeric keypad. To use native input for decimals, there are trade-offs.

  • You can also use type = number to enter multiple decimal points, but only if the numeric value is invalid, the input is invalid, but only android can pop up the numeric keyboard, IOS still pops up the full keyboard. The user experience may be poor.
  • Or use type = text, although you can do pre-input validation (because you can get all the characters), all the models can only play the full keyboard, the user experience is mediocre.
  • Neither of the above two methods can realize the automatic pop-up of the keyboard when entering the page, and it can only be realized by means of the method provided by NA.
  • If you’re a terminal ocD sufferer and a user experience person, then you can build a fake keyboard like I did and none of the above issues will be a problem. You can also add additional functions, such as when the input is in the first digit of the decimal point, the front of the automatic filling of ‘0’; When deleting, if the decimal point is in front of the first digit, automatically add ‘0’; You can also customize cursor colors, keyboard styles, and more.

Unfortunately, I am a terminal patient of OCD. The keyboard kit that is currently implemented has been successfully used in the project as a VUE component. There are single-input and multi-input pages, supporting placeholder and V-Model.