SoFunction
Updated on 2025-04-05

Element Input component analysis summary

The input component is relatively complex. Let's start with a tool library it uses.Conduct analysis.


To calculate the height of the text box, we analyze it from top to bottom according to the code order.

HIDDEN_STYLE

HIDDEN_STYLE is a constant that stores the css style when hidden.

const HIDDEN_STYLE = `
 height:0 !important;
 visibility:hidden !important;
 overflow:hidden !important;
 position:absolute !important;
 z-index:-1000 !important;
 top:0 !important;
 right:0 !important
`;

CONTEXT_STYLE

CONTEXT_STYLE is also a constant used to store the style name to be queried.

const CONTEXT_STYLE = [
 'letter-spacing',
 'line-height',
 'padding-top',
 'padding-bottom',
 'font-family',
 'font-weight',
 'font-size',
 'text-rendering',
 'text-transform',
 'width',
 'text-indent',
 'padding-left',
 'padding-right',
 'border-width',
 'box-sizing'
];

calculateNodeStyling

calculateNodeStyling is used to obtain certain styles of nodes.

function calculateNodeStyling(node) {
 const style = (node); // Get the calculated style of the node, that is, the actual rendering style
 const boxSizing = ('box-sizing'); // Get the value of box-sizing
 // The sum of paddings on the upper and lower const paddingSize = (
  parseFloat(('padding-bottom')) +
  parseFloat(('padding-top'))
 );

 // The width and width of the upper and lower borders (actually the height that looks) const borderSize = (
  parseFloat(('border-bottom-width')) +
  parseFloat(('border-top-width'))
 );

 // Some other styles const contextStyle = CONTEXT_STYLE
  .map(name => `${name}:${(name)}`)
  .join(';');

 return { contextStyle, paddingSize, borderSize, boxSizing };
}

calcTextareaHeight

calcTextareaHeight is the ultimate exposed function to calculate the height of the text field.

export default function calcTextareaHeight(
 targetNode, // The node to be calculated minRows = null, // Minimum number of rows maxRows = null // Maximum number of rows) {
 if (!hiddenTextarea) { // Create a hidden text field, all calculations are performed on it  hiddenTextarea = ('textarea');
  (hiddenTextarea);
 }

 // Get some style values ​​of nodes let {
  paddingSize,
  borderSize,
  boxSizing,
  contextStyle
 } = calculateNodeStyling(targetNode);

 // Set the corresponding style ('style', `${contextStyle};${HIDDEN_STYLE}`);
 // Set content, once according to priority, the value of the node, the placeholder of the node, and the empty string  =  ||  || '';

 // Get scrolling height let height = ;

 if (boxSizing === 'border-box') {
  // If it is border-box, the height must be added with a border  height = height + borderSize;
 } else if (boxSizing === 'content-box') {
  // If it is a content-box, it means that the upper and lower margins must be subtracted.  height = height - paddingSize;
 }

 // Calculate the height of a single line and clear the content first  = '';
 // Use the scroll height to subtract the upper and lower inner margins let singleRowHeight =  - paddingSize;

 if (minRows !== null) { // If the parameter is passed minRows  let minHeight = singleRowHeight * minRows; // It means that there should be at least so many rows of heights  if (boxSizing === 'border-box') { // If it is a border-box, you must add the upper and lower inner margins and the width of the upper and lower borders.   minHeight = minHeight + paddingSize + borderSize;
  }
  height = (minHeight, height); // Take the maximum value of both }
 if (maxRows !== null) { // If the parameter is passed maxRows  let maxHeight = singleRowHeight * maxRows; // It means that at most you can only have so many rows of heights  if (boxSizing === 'border-box') { // If it is a border-box, you must add the upper and lower inner margins and the width of the upper and lower borders.   maxHeight = maxHeight + paddingSize + borderSize;
  }
  height = (maxHeight, height); // Take the minimum value of both }

 // Returns the height that the text field should set return { height: height + 'px'};
};


The input component is quite complicated, so let's analyze it little by little.

life cycle

created

When created, the inputSelect event will be listened to and the inputSelect method will be called.

created() {
 this.$on('inputSelect', );
},

The inputSelect method will call the native select method of the input on refs to select the input.

methods: {
 inputSelect() {
  this.$();
 },
}

mounted

When mounted, the resizeTextarea method will be called to set the size of the text field.

mounted() {
 ();
}
methods: {
 resizeTextarea() {
  if (this.$isServer) return; // If it is server rendering, return directly without performing the following logic  var { autosize, type } = this;
  if (!autosize || type !== 'textarea') return; // If autosize is false, or is not currently a text field, it will also be returned directly  const minRows = ; // Minimum number of rows  const maxRows = ; // Maximum number of rows
   = calcTextareaHeight(this.$, minRows, maxRows); // Calculate the height of the text field and assign values },
}

The outermost layer

The outermost layer is a div with some dynamic classes set on it.

<div :class="[
 type === 'textarea' ? 'el-textarea' : 'el-input',
 size ? 'el-input--' + size : '',
 {
  'is-disabled': disabled,
  'el-input-group': $ || $,
  'el-input-group--append': $,
  'el-input-group--prepend': $
 }
]">
</div>

type

type is a prop, which is set to text by default. If set to textarea, it indicates that it is currently a text field.

props: {
 type: {
  type: String,
  default: 'text'
 },
}

size

size is also a prop, used to set the size of the input box, which is invalid under textarea.

props: {
 size: String,
}

disabled

disabled is also a prop to set whether it is available or not.

props: {
 disabled: Boolean,
}

prepend、append

Both of these are used when setting the input box group. They are passed in through a named slot and placed at the beginning and end of the input respectively.

input

Then, use v-if to render input or textarea respectively according to different types. We first analyze the input part.

Prefixed elements

The prefix element is passed directly through the named slot.

<div class="el-input-group__prepend" v-if="$">
 <slot name="prepend"></slot>
</div>

input icon

The icon is also passed through the named slot, and the icon name can also be passed through the icon in the prop.

<slot name="icon">
 <i
  class="el-input__icon"
  :class="'el-icon-' + icon"
  v-if="icon"
  @click="handleIconClick">
 </i>
</slot>

There is also a click event of handleIconClick, which will trigger the click event:

methods: {
 handleIconClick(event) {
  this.$emit('click', event);
 },
}

input

Then there is the most important input part. Most of the above are props and will not be explained. We will explain the rest one by one.

&lt;input
 v-if="type !== 'textarea'"
 class="el-input__inner"
 :type="type" // type :name="name" // name :placeholder="placeholder" // default value :disabled="disabled" // Whether to disable :readonly="readonly" // Whether to read only :maxlength="maxlength" // Maximum length of input :minlength="minlength" // The minimum length of input (not supported for the time being) :autocomplete="autoComplete" // Automatic completion :autofocus="autofocus" // Autofocus :min="min" // Minimum value allowed (number or date) :max="max" // The maximum value allowed to enter (number or date) :form="form" // The bound form (not native) :value="currentValue" // Enter value ref="input" // Quote @input="handleInput" // Enter an event @focus="handleFocus" // Get focus events @blur="handleBlur" // Lost focus event&gt;

value

SetCurrentValue will be called when the value changes.

watch: {
 'value'(val, oldValue) {
  (val);
 }
},

setCurrentValue is used to change the current value.

methods: {
 setCurrentValue(value) {
  if (value === ) return; // If the old and new values ​​are consistent, return directly
  this.$nextTick(_ =&gt; {
   (); // Reset the text field size during the next DOM update cycle  });

   = value; // Change the current value  this.$emit('input', value); // Trigger input event  this.$emit('change', value); // Trigger change event  ('ElFormItem', '', [value]); // Dispatch events to parent }
}

handleInput

Process input events.

methods: {
 handleInput(event) {
  (); // Change the current value },
}

handleFocus

handleFocus is used to handle events that gain focus and will directly contact the focus event.

methods: {
 handleFocus(event) {
  this.$emit('focus', event);
 },
}

handleBlur

handleBlur is used to handle events that lose focus.

methods: {
 handleBlur(event) {
  this.$emit('blur', event); // Trigger the blur event  ('ElFormItem', '', []); // Send events to the parent component },
}

loading

Loading will determine whether to render based on the computed property validating.

computed: {
 validating() {
  return this.$ === 'validating';
 }
},
<i class="el-input__icon el-icon-loading" v-if="validating"></i>

Post-elements

Post-elements can only be passed in according to the named slot.

<div class="el-input-group__append" v-if="$">
 <slot name="append"></slot>
</div>

Textarea

If the type is set to textarea, the textarea will be rendered. The bounds above are similar to input. I won’t say more. There is an additional textareaStyle, which is calculated based on calcTextareaHeight.

<textarea
 v-else
 class="el-textarea__inner"
 :value="currentValue"
 @input="handleInput"
 ref="textarea"
 :name="name"
 :placeholder="placeholder"
 :disabled="disabled"
 :style="textareaStyle"
 :readonly="readonly"
 :rows="rows"
 :form="form"
 :autofocus="autofocus"
 :maxlength="maxlength"
 :minlength="minlength"
 @focus="handleFocus"
 @blur="handleBlur">
</textarea>

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.