携手创作,共同成长!这是我参与「日新计划 8 月更文挑战」的第17天,点击查看活动详情

简介

输入框组件 Input 通过鼠标或键盘输入表单域内容,提供复合型型输入框,带搜索的输入框,还可以进行大小选择。 本文将分析其源码实现,耐心读完,相信会对您有所帮助。 组件文档 Input gitee源码

更多组件剖析详见 Element 2 源码剖析组件总览

模板HTML

组件的props声明,各属性功能描述详见官方文档Input#attributes。

组件根元素是一个类名为el-textareael-input的 div 元素,元素内容包含了三部分:

  1. 封装原生 input 控件实现单行文本输入框。
  2. 封装原生 textarea 控件多行文本输入文本域。
// packagesinputsrcinput.vue
<template>
  <div :class="[type === 'textarea' ? 'el-textarea' : 'el-input',]" >
    <!-- 单行文本输入框 -->
    <template v-if="type !== 'textarea'">
      <!-- 输入框前置内容 -->
      <div class="el-input-group__prepend"></div> 
      <!-- 表单输入控件 -->
      <input>
      <!-- 输入框头部内容 -->
      <span class="el-input__prefix"></span>
      <!-- 输入框尾部内容 -->
      <span class="el-input__suffix"></span>
      <!-- 输入框后置内容 -->
      <div class="el-input-group__append"></div> 
    </template>
    <!-- 多行文本输入的文本域 -->
    <textarea></textarea>
  </div>
</template>

单行文本输入

单行文本输入框通过封装原生 input 控件实现,支持控件的原生属性。组件通过组合以下多种元素实现文本框 text或密码框password等复合型输入框功能:

  1. 输入框前置内容,提供具名插槽prepend,内容一般为标签或按钮。
  2. 原生 input 表单输入控件,添加了自定义事件。
  3. 输入框头部内容,可以通过prefix-icon或具名插槽prefix增加显示图标。
  4. 输入框尾部内容,可以通过suffix-icon或具名插槽suffix增加显示图标。该元素也用于展示输入框清空Icon、密码显隐切换Icon以及展示输入字数统计。
  5. 输入框后置内容,提供具名插槽append,内容一般为标签或按钮。
<template v-if="type !== 'textarea'">
  <!-- 输入框前置内容 -->
  <div class="el-input-group__prepend" v-if="$slots.prepend">
    <slot name="prepend"></slot>
  </div>
  <!-- 表单输入控件 -->
  <input
    :tabindex="tabindex"  // 元素是否可以聚焦 键盘导航
    v-if="type !== 'textarea'"
    class="el-input__inner"
    v-bind="$attrs" // 透传 Attributes 例如 placeholder
    :type="showPassword ? (passwordVisible ? 'text': 'password') : type" // text/password  也支持其他原生inputtype:disabled="inputDisabled" // 是否禁用
    :readonly="readonly"  // 是否只读
    :autocomplete="autoComplete || autocomplete" // 自动补全
    ref="input"
    @compositionstart="handleCompositionStart" // 输入法编辑器 (IME) 事件
    @compositionupdate="handleCompositionUpdate"
    @compositionend="handleCompositionEnd"
    @input="handleInput" // 输入内容
    @focus="handleFocus" // 获取焦点
    @blur="handleBlur" // 失去焦点
    @change="handleChange" // 输入值变化
    :aria-label="label" // ARIA 无障碍属性
  >
  <!-- 输入框头部内容 -->
  <span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
    <slot name="prefix"></slot>
    <i class="el-input__icon" v-if="prefixIcon"  :class="prefixIcon"></i>
  </span>
  <!-- 输入框尾部内容 -->
  <span class="el-input__suffix" v-if="getSuffixVisible()">
    <span class="el-input__suffix-inner">
      <template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
        <slot name="suffix"></slot>
        <i class="el-input__icon" v-if="suffixIcon" :class="suffixIcon"></i>
      </template>
      // ...
    </span>
    // ...
  </span>
  <!-- 输入框后置内容 -->
  <div class="el-input-group__append" v-if="$slots.append">
    <slot name="append"></slot>
  </div>
</template>

组件渲染效果如下:

Element 2 组件源码剖析之Input输入框

输入框头部/尾部都提供了具名插槽用于增加显示图标,当然也可以传入文本等其他内容,但不建议这么做。

当设置头部/尾部内容时, input 输入框通过属性 padding 提供了 30px 的宽度区域用于内容展示。头部/尾部元素使用绝对布局,将内容偏移覆盖至 padding 区域。

.el-input__prefix {
  position: absolute;
  height: 100%;
  left: 5px;
  top: 0;  
}
.el-input__suffix {
  position: absolute;
  height: 100%;
  right: 5px;
  top: 0; 
}
.el-input--prefix .el-input__inner{
    padding-left: 30px; 
}
.el-input--suffix .el-input__inner{ 
    padding-right: 30px; 
}

Element 2 组件源码剖析之Input输入框

以下示例将自定义文本传入插槽。

<el-input placeholder="请输入内容" v-model="input">
  <template slot="prefix">
    <span style="display: flex; align-items: center; height: 100%">头部内容</span>
  </template>
  <template slot="suffix">
    <span style="display: flex; align-items: center; height: 100%">尾部内容</span>
  </template>
</el-input>

示例渲染出现内容覆盖

Element 2 组件源码剖析之Input输入框

input 元素类型

组件默认使用 text 类型的input控件。当设置 showPassword可得到一个可切换显示隐藏的密码框,内部属性passwordVisible记录显隐状态,根据不同的状态使用 passwordtext 类型。

:type="showPassword ? (passwordVisible ? 'text': 'password') : type"

属性 type 值也可设置为其他原生input的type值。

<el-input v-model="input" placeholder="请输入内容" type="color"></el-input>
<el-input v-model="input" placeholder="请输入内容" type="date"></el-input>
<el-input v-model="input" placeholder="请输入内容" type="datetime-local"></el-input> 
<el-input v-model="input" placeholder="请输入内容" type="file"></el-input> 
<el-input v-model="input" placeholder="请输入内容" type="month"></el-input> 
<el-input v-model="input" placeholder="请输入内容" type="time"></el-input> 
<el-input v-model="input" placeholder="请输入内容" type="week"></el-input>

使用其他原生类型时组件渲染效果,但是一般不建议这么使用!一般组件类库都会提供对应的更加丰富的功能组件,使用库组件页面样式风格更加统一,提升用户交互体验。

Element 2 组件源码剖析之Input输入框

输入法编辑器(IME)事件

输入法在中文、日文和韩文等少数语言中使用。以中文拼音输入法为例,输入的过程大致可以分为组字(composition)提交(commit) 两阶段。比如想打“你好”两个字,会在输入框输入“nihao”的拼音,当输入第一个字母“n”时,组字过程就开始了。此时本地的 IME(input method editor) 软件(比如微软/搜狗拼音输入法)会为我们提供组字框候选列表的 UI 组件。

关于输入法事件更多介绍,请阅读 Web 键盘输入法应用开发指南 —— 输入法事件。

compositionstartcompositionupdatecompositionend是一组事件。

  1. 首先是使用拼音输入法开始输入汉字时 compositionstart 被触发,组字框和候选列表相应出现;

  2. 此后,每按一个新键,就会触发 compositionupdate,此时组字框和候选列表的内容也发生变化;

  3. 当选择了候选列表中的某个字或词,或者敲击空格(中文输入法),compositionend 事件会触发,表明输入被提交。

  4. 当取消输入时,比如使用鼠标单击页面空白处,就会终止当前输入,也会触发compositionend 事件。

属性isComposing值为 true表示用户正在输入。输入结束后(选择字词或者取消),调用方法handleInput,触发组件 input 事件。

// @compositionstart="handleCompositionStart"
// @compositionupdate="handleCompositionUpdate"
// @compositionend="handleCompositionEnd"
handleCompositionStart() {
  this.isComposing = true; // 正在输入
},
handleCompositionUpdate(event) {
  const text = event.target.value;
  const lastCharacter = text[text.length - 1] || '';
  // 韩文字符编码 判断最后一个字符是否特殊的功能键 Process Key 
  this.isComposing = !isKorean(lastCharacter); 
},
handleCompositionEnd(event) {
  // 输入结束后,触发 input 事件
  if (this.isComposing) {
    this.isComposing = false;
    this.handleInput(event);
  }
},

input/change 事件

  • <input><select><textarea> 元素的value被修改时,会触发input事件。
  • 当用户更改<input><select><textarea>元素的值并提交这个更改时,change事件在这些元素上触发。基于表单元素的类型和用户对标签的操作的不同,change事件触发的时机也不同。

对于文本输入元素,比如<input type="text">,每当元素的value改变,input事件都会被触,change事件在控件失去焦点后才会触发。

// @input="handleInput" 
// @change="handleChange"
handleInput(event) {
  // 输入法下用户正在输入
  if (this.isComposing) return;
  // IE 11下 DatePicker组件 hack 写法,详见issues
  // https://github.com/ElemeFE/element/issues/8548
  // should remove the following line when we don't support IE
  if (event.target.value === this.nativeInputValue) return;
  // 触发触发当前实例上的input事件
  this.$emit('input', event.target.value);
  // Input 为受控组件 更新组件的绑定值
  this.$nextTick(this.setNativeInputValue);
},
handleChange(event) {
  // 触发触发当前实例上的change事件
  this.$emit('change', event.target.value);
},

focus/blur 事件

元素获取或失去焦点时,调用事件监听方法,更新内部属性focused记录元素焦点状态,同时触发实例自定义 focusblur 事件。

// @focus="handleFocus"
// @blur="handleBlur"
handleFocus(event) {
  this.focused = true;
  // 触发触发当前实例上的focus事件
  this.$emit('focus', event);
},
handleBlur(event) {
  this.focused = false;
  // 触发触发当前实例上的blur事件
  this.$emit('blur', event);
  if (this.validateEvent) {
    this.dispatch('ElFormItem', 'el.form.blur', [this.value]);
  }
},

关注专栏

如果本文对您有所帮助请关注➕、 点赞、 收藏⭐!您的认可就是对我的最大支持!

此文章已收录到专栏中 ,可以直接关注。