Introduction to the
Originally did not intend to write the input box analysis, I thought an input box can be more complex, but also how to package, then browse the source code, found that there are still a lot of knowledge points I do not know, so I intend to write, the picture below is an Element of the most basic input box
Click here to
Input box source HTML structure
The first step is to understand the HTML structure of the input wrapped with Element. Here is the simplified HTML structure
<template> <div ... > <template v-if="type ! == 'textarea'"> <! --> <div class="el-input-group__prepend" v-if="$slots.prepend">
<slot name="prepend"></slot> </div> <! Input --> <input... > <! --> <span class= "span"el-input__prefix" v-if="$slots.prefix || prefixIcon">... </span> <! --> <span... </span> <! -- trailing element --> <div class="el-input-group__append" v-if="$slots.append">... </div> </template> <textarea v-else> </textarea> </div> </template>Copy the code
Doesn’t it look big? The outer div wraps the inside element in a wrapper, and inside it is the V-if for the Template tag (template doesn’t actually render). At the bottom is the V-else for the textarea, indicating that the type option controls whether the input box component displays an input or a textarea. For the V-else component, there is only one Textarea, and there is nothing to say about it. The key is the v-if. Pre content, post content, and post element. What do they stand for? Here’s the answer
Note the layout of the front and back elements and the body of the input. By modifying the content of the front and back elements, you can see that the width of the middle input is adaptive, as shown below
The width of the left column is variable, and the right column is adaptive
table-cell
display:table
<div style="display:table" class='wrapper'>
<div style="display:table-cell" class='left'>
</div>
<div style="display:table-cell" class='right'>
</div>
</div>
Copy the code
This layout can also be done in Flex, where the left element has no width and the right element has Flex :1. Let’s look at the CSS of the input box
-webkit-appearance:none,outline:none
transition
ease
Disable the implementation of the state
Disabling is simple, controlled by the user passing in the Disabled property as shown in the following code
<el-input
placeholder="Please enter the content"
v-model="input1"
:disabled="true">
</el-input>
Copy the code
inputDisabled() {
return this.disabled || (this.elForm || {}).disabled;
},
Copy the code
The reason for this is to determine that if the input is included in the form, if the form is disabled, then it is disabled. Disabling input box styles is controlled by the outermost div class
<div :class=[{'is-disabled': inputDisabled}... ] >... </div>Copy the code
There is no control over the input, because you can control the textarea and the input by placing it in the outer layer, reducing code redundancy. You can control the input and the Textarea by selecting them in the placeholder color, but pay attention to compatibility
&::placeholder {
color: $--input-disabled-placeholder-color;
}
Copy the code
Attribute of the input element
You can learn a lot by looking at the native input properties in the component
<input
:tabindex="tabindex"
v-if="type ! == 'textarea'"
class="el-input__inner"
v-bind="$attrs"
:type="type"
:disabled="inputDisabled"
:readonly="readonly"
:autocomplete="autoComplete"
:value="currentValue"
ref="input"
@compositionstart="handleComposition"
@compositionupdate="handleComposition"
@compositionend="handleComposition"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
:aria-label="label"
>
Copy the code
This is what a mature component would need to do. Tabindex controls the order of access when the TAB key is pressed. If tabIndex is set to negative, it cannot be accessed by the TAB key, and if it is set to 0, it is accessed last. Then v – if = “type! == ‘textarea'” controls whether the input is rendered or not. The user passes in the type attribute to control the input class el-input__inner, as described earlier, and then the v-bind=”$attrs” statement. Read the official website
<el-input maxlength="5" minlength="2">
</el-input>
Copy the code
Here we give the < el – input > component added two primary attributes, pay attention to the two primary attribute is not inside the prop, the two attributes is the maximum length of input and the minimum input control input, so these two attributes just now on the parent element < el – input >, How do you pass it to the native input child inside the < el-Input > element? Without passing, these two attributes have no effect because they do not exist on the child input. The answer is to use v-bind=”$attrs”, which binds all non-prop features of the parent element to the input element, otherwise you have to declare maxLength,minlength in props, and the code gets too big. This is where $attrs comes in handy :readonly=”readonly” :autocomplete=” autocomplete “. These two attributes are native, passed in by the user, and control whether the input field is read-only and auto-complete. And then the input field value:value=”currentValue” where currentValue is in data
currentValue: this.value === undefined || this.value === null
? ' '
: this.value,
Copy the code
If the user does not write v-model on
, then no value is passed in, so currentValue is an empty string, otherwise it is the value passed in, and ref=”input”,ref is used to register references to elements or child components. The reference information will be registered in the parent component’s $refs object, so that subsequent code can access the native INPUT DOM directly
@compositionstart="handleComposition"
@compositionupdate="handleComposition"
@compositionend="handleComposition"
Copy the code
The compositionStart event is emitted before the input of a text (similar to the keyDown event, but only before the input of several visible characters). The input of these visible characters may require a series of keyboard operations, speech recognition, or clicking on the alternative word of the input method. In simple terms, when switching the Chinese input method, compositionStart will be triggered at the beginning of the Struggle sound (at this time, the input has not been filled with the real content), and then each type of pinyin letter, Compositionupdate is triggered, and comPositionEnd is triggered when the finished Chinese is entered into the input. When compositionStart is triggered, the text box is filled with “virtual text” (text to be acknowledged) and the input event is triggered. When comPositionEnd is triggered, the actual content is filled in (acknowledged text), so if you don’t want the input event to be triggered, you have to set a bool variable to control that
<el-input v-model="inputValue"></el-input>
handleComposition
handleComposition(event) {
if (event.type === 'compositionend') {
this.isOnComposition = false;
this.currentValue = this.valueBeforeComposition;
this.valueBeforeComposition = null;
this.handleInput(event);
} else {
const text = event.target.value;
const lastCharacter = text[text.length - 1] || ' '; this.isOnComposition = ! isKorean(lastCharacter);if (this.isOnComposition && event.type === 'compositionstart') { this.valueBeforeComposition = text; }}},Copy the code
Here we define a bool isOnComposition in data. This variable is used to determine whether during the struggle sound, the initial value is false. When the struggle sound starts, the compositionStart event is triggered. Update isOnComposition with this.isOnComposition =! IsKorean (lastCharacter), where the logic is to determine whether the lastCharacter of the input character isKorean, Korean is determined by regular expression, as for why to determine the lastCharacter of Korean, it is not clear ~ if it is Chinese, isOnComposition is true, A valueBeforeComposition variable is used to save the current text in the input before typing when the compositionStart event is in the middle of a struggle tone. The role of valueBeforeComposition is described below. Look at if (event.type === ‘compositionEnd ‘). Trigger compositionEnd, set isOnComposition to False to indicate that typing is done, and notice that a this.handleInput(event) is manually triggered (handleInput is the v-on:input bound to input). This is because the compositionEnd will fire after the input event when the final input is completed, and isOnComposition will still be true. This will not trigger the emit in the following handleInput to pass the new input value to the parent component. So you need to call handleInput manually, please understand carefully!
handleInput(event) {
const value = event.target.value;
this.setCurrentValue(value);
if (this.isOnComposition) return;
this.$emit('input', value);
},
Copy the code
It is reasonable and normal that the EMIT event should not be emitted in handleInput when isOnComposition is true indicating that struggle is being entered
A cleanable implementation
< el-Input > if the clearable attribute is added, a cross icon will appear after the input text, and the input content will be cleared after clicking, as shown below
<! --> <span class="el-input__suffix"
v-if="$slots.suffix || suffixIcon || showClear || validateState && needStatusIcon">
<span class="el-input__suffix-inner">
<template v-if=! "" showClear">
<slot name="suffix"></slot>
<i class="el-input__icon"
v-if="suffixIcon"
:class="suffixIcon">
</i>
</template>
<i v-else
class="el-input__icon el-icon-circle-close el-input__clear"
@click="clear"
></i>
</span>
<i class="el-input__icon"
v-if="validateState"
:class="['el-input__validateIcon', validateIcon]">
</i>
</span>
Copy the code
The middle < I > is the empty button, it is an I label, there is a click event, the front through the showClear to determine whether to display the empty button, the logic is as follows
showClear() {
returnthis.clearable && ! this.disabled && ! this.readonly && this.currentValue ! = =' ' &&
(this.focused || this.hovering);
}
Copy the code
This calculation attributes first step depends on whether the user to add the properties of the display delete button, if not then don’t show, if you have continued to judge, in the disabled and the read-only state and the current input value is not empty and only the input gains focus or mouse just showed up, condition slightly more Then watch the clear to empty this method
clear() {
this.$emit('input'.' ');
this.$emit('change'.' ');
this.$emit('clear');
this.setCurrentValue(' ');
this.focus();
}
Copy the code
< EL input v-model=”v”>
the 3rd emit triggers the @clear method of the parent component to let it know that it has cleared. The fourth sentence updates its currentValue to null, and the fifth game gives input focus to facilitate input
Highly adaptive implementation of textarea
This one is a bit more difficult. Here’s a quick look at how it works. The native Textarea will have scroll bars as the content increases
function calcTextareaHeight(){
...
letheight = hiddenTextarea.scrollHeight; const result = {}; . result.height = `${ height }px`;
return result
}
Copy the code
We make height equal to scrollHeight, which is the height of the scrollbar, so we make the height bigger, and then we return that height and bind it to the input style to dynamically change the height of the textarea. Refer to the lot