Why fake keyboards?

Or input box, cursor all false false keyboard?

You don’t have to write a fake for your phone, do you?

Show off?



Background of the problem


Demand points of mobile TERMINAL H5 project:

Enter a page automatically pop up with a decimal point numeric keyboard, and its own input validation, such as the amount – can only input numbers and decimal point, and can only input a decimal point, decimal not more than 2, and input before the validation is not legal do not let input, (UE special plus function – custom cursor color). The breakdown is as follows:

  • On the related page, the focus is automatically displayed
  • Keyboard automatically pops up
  • Pop up numeric keypad with decimal point
  • Digit input before automatic verification, can only enter a decimal point, the number of decimal number does not exceed 2, more than the input cannot continue
  • If the cursor is first and you type a ‘.’, the ‘0’ and ‘.’ are automatically added.

Realization programming


1. Mobile phone keyboard implementation scheme based on input +


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

(2) For function point 2, set the attribute autofocus for input, but the keyboard will not pop up automatically;

You must manually click the input box keyboard will pop up; When entering the page, use js to trigger click or foucus to find the key

Disk will not automatically pop up, delay click, focus did not pop up; So there’s only one last option — let NA carry it

A method for making the keyboard pop up. Pure front-end can not be done, need NA end assistance /, or find PM PK to eliminate the need for automatic keyboard play

>.< (barely acceptable)

(3) For function point 3, type = “number” or type = “tel” can be set for the numeric keypad; The former

Android can pop up the numeric keypad on the ios side can only play the full keyboard, the latter on Android and ios pop up are numeric keys

Dish, but!! The pop-up numeric keypad has no decimal point! (My Huawei honor 9 is very force to play for me

Type = “number” is the only option available on the keyboard

(4) For function point 4, set type = “number”, find that you can input the decimal point repeatedly, look really insane, the first time input decimal point does not automatically change to ‘0.’

                      

Figure 1 Native Input Type =number effect


You’d be wise to use events to listen for typed characters, judge them before typing, and then decide

Whether to put the input box.

Again, you’ll be happy to think of a bunch of events that might be useful: onKeyDown, onKeyUp, onchange, onInput,

(onpropertychange.

                                                    

It’s a long way to go. After repeated attempts, I still find many problems.

  • Onkeyup — this function is triggered every time a character is added or deleted, but it is triggered only after the value is entered.
  • Onchange – this is triggered when the content changes (it may be equal twice) and loses focus. There is no validation before typing.
  • Onpropertychange — The onChange event fires when the content changes (it may still be equal twice) and loses focus; This event is emitted every time a character is added or deleted, and is also emitted by js changes, but this event is exclusive to IE.
  • Oninput – not supported on many mobile phones.

(That leaves onKeyUp /textInput with a silver lining >.<.)

  • Onkeyup — Its event has two related attributes, Event.key and Event.keyCode. Event.key didn’t work on my Huawei Honor 9 phone (as you can imagine on other lower versions). But it also has a property, Event. keyCode, whose value on the PC side is the ASCII code for the typed character. But entering any number or decimal point on the phone is 229 (huawei Honor 9 test), so onKeyUp doesn’t work either.
  • OntextInput – on BOTH PC and mobile!! Its event. Data gets the input value. Happy ~ ~

What a relief, as long as you can get the value before typing it.

Write the verification process in one breath 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


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


Failed to get all characters in the input box by value


The value of input type = number can only be a numeric value.

So if you say ’12.’ and value is ’12’, and you just say ‘.’ and value is an empty string,

Can’t get the decimal point.

So you can’t tell if you want to put in a decimal point, so you can put in an infinite number of decimal points

Decimal point, the problem is still not solved.


Try:

  • Use the two-way binding this.amount in VUE to get all the input characters. This. amount gets the same value as value. The attempt failed.
  • Maintains an array of characters for the input values obtained by textInput. But textInput does not fire on deletion, so it does not get all the exact characters in the input field in real time; Moreover, the character array cannot be maintained accurately because we cannot determine which character is deleted because we cannot obtain the specific position of the cursor in the input box. The attempt failed.


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


Therefore, it is difficult to realize the implementation scheme based on input + mobile phone keyboard to meet the above requirements.


2. Implementation scheme based on input + false numeric keyboard

To use a fake keyboard with a native input field, do the following:

  • Disable the keyboard of the mobile phone
  • Gets the content of the Input box

Disable the built-in keyboard of the mobile phone, and set the readonly attribute of the Input if the NA exposed method is not supported. this

In this case, the input box can not add delete characters.

If the NA terminal can provide a method to disable the built-in keyboard of the mobile phone, the input box can be added and deleted by clicking the fake keyboard

In addition to the characters.

If you just add and delete from the back, you can easily do this by concatenating the character corresponding to the keyboard click into Input type=text

The same is true if the value is deleted.

But if the cursor isn’t in the last digit, it’s in the middle

                                           

Figure 2 example of a cursor in the middle of a number

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.

Document. Selection and selectionStart are only supported by Internet Explorer and Firefox.

// 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


As our mobile terminal H5 development project, considering compatibility, it is obvious that the above method is not compatible with most models.


3. Complete false implementation scheme of input box, cursor and numeric keyboard

The above two schemes are difficult to achieve, so I can only dare to imagine, in order to meet the above requirements of the fake keyboard will have to achieve false input

A set of equipment for entering frame, false cursor and false keyboard. So I can control all the elements, all the above problems

Part can be solved.


A prototype

It is very easy to remove a dummy keyboard without a cursor if the implementation can only add it from the back, just bind a click event to each key

Object, maintains an array, and maintains the contents of the input field each time it is pushed or popped from behind.

                                            

Figure 3. The renderings can only be added and removed from the end without the cursor

But the experience is worse than the effect of the real input box.


The difficulties in

In order to achieve the same experience as a native keyboard with its own input verification, the main difficulties are:

  • There is a cursor and the cursor flashes
  • Cursor position, click a number in the middle of the cursor automatically move over
  • Delete based on cursor position
  • Lose focus cursor hiding, click the input box cursor display and pop up the keyboard


Native JS implementation

For the cursor implementation, create an element that sets the background color and can control it to hide and appear.

For “clicking on a number automatically moves the cursor over”, you can add an empty element space with the click event for each number or decimal point, and then add the character to be entered. Space is used to bind a click event that tells the cursor where to move.

// Enter characters before the cursorfunction insert(value) {
	var span = document.createElement("span"); // Create an element containing the value sp.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); //insert null inputarea. insertBefore(span, cursor); / / insert value}Copy the code

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

// Delete elementsfunction 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 process of adding and removing elements in Chrome.

                

FIG. 4 Changes of add, delete and cursor movement elements

Each space element is bound to a click event that moves the cursor, and there’s a right-space that you can put in the placeholder, or you can add a click event that always moves the cursor to the last bit.

// Move cursor positionfunction 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 timed task. Cursor flashing is set as follows, using the native setInterval implementation.

// Set the cursor timing 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 with input box, cursor, keyboard false number keyboard.

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

Input box to get focus, cursor flash, pop-up keyboard; Out of focus The cursor disappears.

Why not use jQuery?

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

Second, because you rarely need to manipulate the DOM directly after using VUE, a few methods are more lightweight to implement themselves, if only for use

One or two ways to introduce jQuery will make the project heavier.


Native JS implementation effect

                                            

FIG. 5 Effect diagram of input box, cursor and keyboard of native JS

Source github.com/DaisyWang88…

Mobile phone scan verification:

The sandbox. Runjs. Cn/show/mvjrca… (Chrome plugin URL QR code generator getcrx.cn)


The native JS implementation is a bit sloppy due to the 300 ms delay of the click event on the mobile side. If using native

JS implementations require fastClick or Zepto tap events to resolve latency issues.


PS: IT was said before that “VUE itself solves the 300-millisecond delay problem”, but it was found wrong after research. I’m really sorry for the trouble caused to you.

VUE click events are native click events that do not handle this delay.

In order not to confuse you, the Demo on Github has fixed the latency issue with fastClick (too lazy before >.<). Now the native JS implementation is also very smooth.

VUE componentization


Considering that some application scenarios in the project have multiple input boxes, of course only one keyboard is required for input, so componentized

The input box is used as a component V-input and the keyboard as a component V-keyboard.


Input field and keyboard interaction

The interaction diagram is as follows:

               

Figure 6 Interaction diagram of VUE components

Considering that there is a scenario of multiple input boxes on a page in this project, it is necessary to control which input box the keyboard works with

To use.

To achieve this goal, use “when clicking the input box to get focus, the current V-input box component of the real

Example to v-keyboard component “.

this.$refs.virtualKeyBoard.$emit('getInputVm', this.$refs.virtualInput);
Copy the code

As shown in Figure 6, the V-Keyboard component listens for the ‘getInputVm’ event to get an instance of v-Input.

After obtaining an instance of the input box component V-input, you can add according to the click event of the keyboard

Add or delete, operate the input box component V-input to put or delete characters.

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 focus and the keyboard automatically pops up


When entering a page, the input box will automatically get the focus and the keyboard will pop up automatically.

  • The input box automatically obtains the focus. You can set is-auto-focus to control whether the input box automatically obtains the 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 page instantiation is complete.
this.$refs.virtualKeyBoard.$emit('getInputVm', this.$refs.virtualInput);
Copy the code

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

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


V – model to support


Vue supports custom V-models, sub-components that set up a value props.

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

$emit an ‘input’ event when value changes and then 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:

                                               

Figure 7 Effect diagram of VUE version

See github.com/DaisyWang88… .

conclusion

The native input set type = number, which controls the number of decimal points and decimal places before input

It is difficult to implement, and there are compatibility issues to get the value before input. At present, only ontextInput can be input on the mobile terminal

The key problem is that the value type = number does not contain the decimal point, causing input

Before using regular validation is almost impossible to implement; If type= text is set, all characters in the input box will be retrieved

Method popup numeric keypad. To enter decimals using native input, you have to make a trade-off.

  • You can use type = number to input multiple decimal points, and only indicate that the input is invalid if the value is invalid. However, only Android can pop up the numeric keyboard, while IOS can still pop up the full keyboard. The user experience may be poor.
  • Or use type = text, although it can be done before input verification (because you can take all characters), but all models can only play the full keyboard, the user experience is also general.
  • The above two methods cannot automatically pop up the keyboard when entering the page, and can only be realized by means of the method provided by NA.
  • If you’re obsessive-compulsive, user-experience oriented, then you can build a fake keyboard like I did, and none of the above is a problem. You can also add additional functions, such as when you enter the decimal point at the first point, before the automatic ‘0’; When deleting, if the decimal point is in front of the first digit, auto-fill ‘0’; You can also customize cursor colors, keyboard styles, and more.

The keyboard suite currently implemented is transformed into a VUE component and has been successfully implemented in the project

Support placeholder and V-Model; support placeholder and V-Model.