Element Input组件分析小结

input组件相对来说复杂一点,我们先从它用到的一个工具库calcTextareaHeight.js进行分析。

calcTextareaHeight.js

calcTextareaHeight.js使用来计算文本框的高度的,我们根据代码顺序从上往下进行分析。

HIDDEN_STYLE

HIDDEN_STYLE是一个常量,存储隐藏时候的css样式的。

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也是一个常量,用来存储要查询的样式名。

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用来获取结点的某些样式。

function calculateNodeStyling(node) {
 const style = window.getComputedStyle(node); // 获取结点的计算后的样式,即实际渲染的样式

 const boxSizing = style.getPropertyValue('box-sizing'); // 获取 box-sizing 的值

 // 上下的 padding 之和
 const paddingSize = (
  parseFloat(style.getPropertyValue('padding-bottom')) +
  parseFloat(style.getPropertyValue('padding-top'))
 );

 // 上下的边框宽度和(其实是看上去的高度)
 const borderSize = (
  parseFloat(style.getPropertyValue('border-bottom-width')) +
  parseFloat(style.getPropertyValue('border-top-width'))
 );

 // 其他一些样式
 const contextStyle = CONTEXT_STYLE
  .map(name => `${name}:${style.getPropertyValue(name)}`)
  .join(';');

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

calcTextareaHeight

calcTextareaHeight是最终暴露出去的函数,用来计算文本域的高度。

export default function calcTextareaHeight(
 targetNode, // 要计算的结点
 minRows = null, // 最小的行数
 maxRows = null // 最大的行数
) {
 if (!hiddenTextarea) { // 来创建一个隐藏的文本域,所有的计算都是在这上面进行的
  hiddenTextarea = document.createElement('textarea');
  document.body.appendChild(hiddenTextarea);
 }

 // 获取结点一些样式值
 let {
  paddingSize,
  borderSize,
  boxSizing,
  contextStyle
 } = calculateNodeStyling(targetNode);

 // 设置相应的样式
 hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
 // 设置内容,按优先级一次是 结点的 value, 结点的 placeholder, 以及空字符串
 hiddenTextarea.value = targetNode.value || targetNode.placeholder || '';

 // 获取滚动高度
 let height = hiddenTextarea.scrollHeight;

 if (boxSizing === 'border-box') {
  // 如果是 border-box,说明高度得加上边框
  height = height + borderSize;
 } else if (boxSizing === 'content-box') {
  // 如果是 content-box,说明得减去上下内边距
  height = height - paddingSize;
 }

 // 计算单行高度,先清空内容
 hiddenTextarea.value = '';
 // 再用滚动高度减去上下内边距
 let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

 if (minRows !== null) { // 如果参数传递了 minRows
  let minHeight = singleRowHeight * minRows; // 说明最少应当有这么多行的高度
  if (boxSizing === 'border-box') { // 如果是 border-box,还得加上上下内边距和上下边框的宽度
   minHeight = minHeight + paddingSize + borderSize;
  }
  height = Math.max(minHeight, height); // 取二者最大值
 }
 if (maxRows !== null) { // 如果参数传递了 maxRows
  let maxHeight = singleRowHeight * maxRows; // 说明最多只能有这么多行的高度
  if (boxSizing === 'border-box') { // 如果是 border-box,还得加上上下内边距和上下边框的宽度
   maxHeight = maxHeight + paddingSize + borderSize;
  }
  height = Math.min(maxHeight, height); // 取二者最小值
 }

 // 返回文本域应当设置的高度
 return { height: height + 'px'};
};

input.vue

input组件较为繁琐,我们一点点分析。

生命周期

created

创建的时候会监听inputSelect事件,并调用inputSelect方法。

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

inputSelect方法会调用refs上的input的原生的select方法,来选中该input。

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

mounted

挂载的时候,会调用resizeTextarea方法来设置文本域的大小。

mounted() {
 this.resizeTextarea();
}
methods: {
 resizeTextarea() {
  if (this.$isServer) return; // 如果是服务端渲染,直接返回,不进行下面的逻辑
  var { autosize, type } = this;
  if (!autosize || type !== 'textarea') return; // 如果 autosize 是 false,或者当前不是文本域,也直接返回
  const minRows = autosize.minRows; // 最少行数
  const maxRows = autosize.maxRows; // 最大行数

  this.textareaStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows); // 计算文本域的高度,并赋值
 },
}

最外层

最外层是一个div,上面设置了一些动态的class。

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

type

type是一个prop,它默认设置为text,如果设置为textarea,表明当前是一个文本域。

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

size

size也是一个prop,用来设置输入框的大小,在textarea下无效。

props: {
 size: String,
}

disabled

disabled也是一个prop,用来设置是否可用。

props: {
 disabled: Boolean,
}

prepend、append

这两个都是在设置输入框组的时候使用的,通过具名slot传入,分别放置于input的首和尾。

input

然后,根据type的不同使用v-if分别渲染input或者textarea,我们先分析input部分。

前置元素

前置元素直接通过具名slot传入。

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

input 图标

图标也是通过具名slot传入的,也可以通过prop中的icon传入图标名。

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

上面还绑定了一个handleIconClick的点击事件,它会触发click事件:

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

input

然后是最重要的input部分,上面大部分是prop,不进行讲解,其余的我们将一一讲解。

<input
 v-if="type !== 'textarea'"
 class="el-input__inner"
 :type="type" // 类型
 :name="name" // 名字
 :placeholder="placeholder" // 默认值
 :disabled="disabled" // 是否禁用
 :readonly="readonly" // 是否只读
 :maxlength="maxlength" // 输入的最大长度
 :minlength="minlength" // 输入的最小长度(暂时不支持)
 :autocomplete="autoComplete" // 自动补全
 :autofocus="autofocus" // 自动聚焦
 :min="min" // 允许输入的最小值(数字或者日期)
 :max="max" // 允许输入的最大值(数字或者日期)
 :form="form" // 绑定的表单(不是原生的)
 :value="currentValue" // 输入值
 ref="input" // 引用
 @input="handleInput" // 输入事件
 @focus="handleFocus" // 获得焦点事件
 @blur="handleBlur" // 失去焦点事件
>

value

value改变的时候会调用setCurrentValue。

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

而setCurrentValue是用来改变当前值的。

methods: {
 setCurrentValue(value) {
  if (value === this.currentValue) return; // 如果新旧值一致直接返回

  this.$nextTick(_ => {
   this.resizeTextarea(); // 下一个DOM更新周期时,重新设置文本域大小
  });

  this.currentValue = value; // 改变当前值
  this.$emit('input', value); // 触发 input 事件
  this.$emit('change', value); // 触发 change 事件
  this.dispatch('ElFormItem', 'el.form.change', [value]); // 向父级的派发 el.form.change 事件
 }
}

handleInput

处理输入事件。

methods: {
 handleInput(event) {
  this.setCurrentValue(event.target.value); // 改变当前值
 },
}

handleFocus

handleFocus用来处理获得焦点的事件,会直接触发focus事件。

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

handleBlur

handleBlur用来处理失去焦点的事件。

methods: {
 handleBlur(event) {
  this.$emit('blur', event); // 触发 blur 事件
  this.dispatch('ElFormItem', 'el.form.blur', [this.currentValue]); // 向父组件派发 el.form.blur 事件
 },
}

loading

loading会根据计算属性validating来决定是否渲染。

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

后置元素

后置元素只能根据具名slot传入。

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

Textarea

如果type设置为textarea则会渲染textarea,上面绑定的都和input类似,不再多说,多了一个textareaStyle,是根据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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • javascript createElement()创建input不能设置name属性的解决方法

    其中obj.name = "select";这句话并不能起到预期的作用,无论是看生成后的HTML代码还是观察使用document.getElementsByName()获取的数组长度,都会发现这个name设置并没有起作用.当然这种情况只出现在IE6和IE7中,而在FireFox中是没有问题的. Test function addInput(){ var div = document.getElementById("testDiv"); var obj = docum

  • vue使用element-ui的el-input监听不了回车事件的解决方法

    原因 今天在使用element-ui时,el-input组件监听不了回车事件,如下代码没有想要的效果: <el-input class="search-input" placeholder="请输入内容" v-model="searchText" @keyup.enter="search()"></el-input> 原因应该是element-ui自身封装了一层input标签之后影响了事件的监听,在el

  • vue Element-ui input 远程搜索与修改建议显示模版的示例代码

    html: <template> <el-autocomplete popper-class="my-autocomplete" custom-item="my-remote" v-model="state" :fetch-suggestions="querySearch" placeholder="默认空" icon="close" :on-icon-click=&q

  • vue2.0结合Element实现select动态控制input禁用实例

    今天有一个盆友问小颖,怎么实现用select动态控制input禁用,也就是说,input默认是可编辑的,但是每当我选一次select,input就会变成禁用,虽然小颖不知道她为什么这样做,因为小颖觉得为什么不直接把input设置成禁用的而要用动态的,选一次select禁用一次input,也就是说,input只有在select是没有点击过的时候是可编辑的,但凡我改变一次select的值,input就要被设置成禁用,其实没有必要,因为第一次设置成禁用后面已经不能再改变input的值了,不过当时小颖也

  • element el-input directive数字进行控制

    使用自定义指令格式化el-input 背景 使用element开发的过程中遇到循环的数据只能输入数字,并且有不要小数点,有需要小数点的 使用vue directive 进行控制 开发 页面使用方式 v-numberInt:0="item.first_fee" 0为保留几位小数 <tr v-for="(item,index) in form.valuation_rules" :key="index"> <td class=&quo

  • Element Input组件分析小结

    input组件相对来说复杂一点,我们先从它用到的一个工具库calcTextareaHeight.js进行分析. calcTextareaHeight.js calcTextareaHeight.js使用来计算文本框的高度的,我们根据代码顺序从上往下进行分析. HIDDEN_STYLE HIDDEN_STYLE是一个常量,存储隐藏时候的css样式的. const HIDDEN_STYLE = ` height:0 !important; visibility:hidden !important;

  • 基于element UI input组件自行封装“数字区间”输入框组件的问题及解决

    目录 问题描述 实现效果 实现代码 问题描述 在开发时遇到一个数字区间输入框的需求,如下图: 项目使用的是vue,组件库用的是element UI,但是element UI并没有提供数字区间组件,只提供了InputNumber 计数器输入框,如果用两个计数器输入框进行拼接也能满足需求,但是样式调试起来太过于复杂且不够灵活,不能令人满意,并且该数字区间输入框在其它界面也有这种需求,于是就在element input输入框的基础上自行封装了一个数字区间组件使用. 实现效果 实现效果如下: 使用方式如

  • js实现Element中input组件的部分功能并封装成组件(实例代码)

    现在实现的有基础用法.可清空.密码框,参考链接:https://element.eleme.cn/#/zh-CN/component/input HTML代码:想要测试哪个组件,直接将对应组件解开注释即可,标红的js和css记得修改成你自己的位置. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>js实现可清空input组件</title> &

  • 基于Vue 2.0的模块化前端 UI 组件库小结

    AT-UI 是一款基于 Vue.js 2.0 的轻量级.模块化前端 UI 组件库,主要用于快速开发 PC 网站产品. 专门为桌面应用程序构建,AT-UI 提供了一套 npm + webpack + babel 前端开发工作流程,以及一个体面的干净整洁的 UI 组件. 特性 基于 Vue 开发的 UI 组件 基于 npm + webpack + babel 的工作流,支持 ES2015 CSS 样式独立,保证不同的框架实现都能保持统一的 UI 风格( 详见: AT-UI Style) 提供友好的

  • vue实现一个获取按键展示快捷键效果的Input组件

    遇到一个需求,页面内要自定义快捷键,这就需要可以有地方设置和展示快捷键,找了一圈Element UI发现没有能稍微改改就能用的组件,所以自己动手写了一个. 这个只有快捷键展示功能,快捷键实际绑定生效的话是依赖传回的快捷键数据,由另外的组件处理的.目前只测试了Chrome的环境. 效果如下: 关键点 虽然看起来像是一个Input但在组件内实际上是展示一个标签效果,还需要有删除按钮.这就得在输入框内放下html代码,浏览器的Input组件显然不适合,这就只能自己仿一个类Input组件效果了. foc

  • Vue Element前端应用开发之常规Element界面组件

    目录 1.列表界面和其他模块展示处理 2.常规界面组件的使用 1)表单和表单项.单文本框 2)下拉列表控件的绑定 3)图片展示 4)第三方扩展控件 3.自定义组件的创建使用 1.列表界面和其他模块展示处理 常规的列表展示界面,一般分为几个区域,一个是查询区域,一个是列表展示区域,一个是底部的分页组件区域.查询区域主要针对常规条件进行布局,以及增加一些全局或者批量的操作,如导入.导出.添加.批量添加.批量删除等按钮:而其中主体的列表展示区域,是相对比较复杂一点的地方,需要对各项数据进行比较友好的展

  • Element Plus组件Form表单Table表格二次封装的完整过程

    目录 前言 Form表单的封装 简述 正常的使用 开始封装① 开始封装② 开始封装③ 开始封装④ 完整封装代码⑤ 配置项类型文件 配置项文件 form表单组件文件 page-search组件文件 role页面组件文件 结语 Table表格的封装 简述 正常使用 开始封装① 开始封装② 开始封装③ 完整封装代码④ 配置项类型文件 配置项文件 table表单组件文件 page-table组件文件 user页面组件文件 结语 前言 直至今天,看了一下别人写的代码,才发现以前自己写的代码太垃圾,所以在这

  • 微信小程序 (十七)input 组件详细介绍

    input输入框使用的频率也是比较高的...样式的话自己外面包裹个view自己定义.input属性也不是很多,有需要自己慢慢测,尝试 主要属性: wxml <!--style的优先级比class高会覆盖和class相同属性--> <view class="inputView" style="margin-top: 40% "> <input class="input" type="number"

  • Vue中封装input组件的实例详解

    Vue中封装input组件 最近有点忙不过来 脱了很久,没有更新 抱歉.今天要将的时如何自定义封装input组件 ,博主知识发个简单的模板 码友们可以更具自己的实际项目添加需要的参数 我的项目中的UI图是这样的 代码如下 子组件的模板设置 <template> <div class="completion-input-box"> <span class="input-box-name">{{text}}</span>

  • 在laravel中使用Symfony的Crawler组件分析HTML

    Crawler全名是DomCrawler,是Symfony框架的组件.令人发指的是DomCrawler的没有中文文档,Symfony也没有翻译该部分,所以使用DomCrawler开发只能一点一点摸索,现将使用过程中的经验总结. 首先是安装 composer require symfony/dom-crawler composer require symfony/css-selector css-seelctor 是 css选择器,用css选择节点时一些函数会用到 手册里面使用的例子是 use S

随机推荐