Element Input组件分析小结


Posted in Javascript onOctober 11, 2018

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>

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

Javascript 相关文章推荐
ImageZoom 图片放大镜效果(多功能扩展篇)
Apr 14 Javascript
通过上下左右键和回车键切换光标实现代码
Mar 08 Javascript
javascript计时器事件使用详解
Jan 07 Javascript
搭建pomelo 开发环境
Jun 24 Javascript
js获得当前系统日期时间的方法
May 06 Javascript
jQuery蓝色风格滑动导航栏代码分享
Aug 19 Javascript
JS实现新浪微博效果带遮罩层的弹出框代码
Oct 12 Javascript
移动端点击态处理的三种实现方式
Jan 12 Javascript
VUE 使用中踩过的坑
Feb 08 Javascript
vue轻量级框架无法获取到vue对象解决方法
May 12 Javascript
js+css实现扇形导航效果
Aug 18 Javascript
JS前端canvas交互实现拖拽旋转及缩放示例
Aug 05 Javascript
element el-input directive数字进行控制
Oct 11 #Javascript
详解angular2.x创建项目入门指令
Oct 11 #Javascript
详解vscode中vue代码颜色插件
Oct 11 #Javascript
微信小程序之裁剪图片成圆形的实现代码
Oct 11 #Javascript
Vue中使用ElementUI使用第三方图标库iconfont的示例
Oct 11 #Javascript
css配合JavaScript实现tab标签切换效果
Oct 11 #Javascript
vue最简单的前后端交互示例详解
Oct 11 #Javascript
You might like
PHP教程 基本语法
2009/10/23 PHP
浅析php数据类型转换
2014/01/09 PHP
php-fpm配置详解
2014/02/12 PHP
php的socket编程详解
2016/11/20 PHP
PHP面向对象之里氏替换原则简单示例
2018/04/08 PHP
JavaScript延迟加载
2021/03/09 Javascript
javascript 冒号 使用说明
2009/06/06 Javascript
ExtJS扩展 垂直tabLayout实现代码
2009/06/21 Javascript
Js控制弹窗实现在任意分辨率下居中显示
2013/08/01 Javascript
Table冻结表头示例代码
2013/08/20 Javascript
ajax与302响应代码测试
2013/10/23 Javascript
js实现简单登录功能的实例代码
2013/11/09 Javascript
JS中typeof与instanceof之间的区别总结
2013/11/14 Javascript
JQuery标签页效果的两个实例讲解(4)
2015/09/17 Javascript
Bootstrap实现默认导航栏效果
2020/09/21 Javascript
Javascript将双字节字符转换成单字节字符并计算长度
2016/06/22 Javascript
vue2项目使用sass的示例代码
2017/06/28 Javascript
node.js基于socket.io快速实现一个实时通讯应用
2019/04/23 Javascript
antd design table更改某行数据的样式操作
2020/10/31 Javascript
React服务端渲染原理解析与实践
2021/03/04 Javascript
浅析Python中的for 循环
2016/06/09 Python
Django数据库表反向生成实例解析
2018/02/06 Python
python将txt文档每行内容循环插入数据库的方法
2018/12/28 Python
Python3 hashlib密码散列算法原理详解
2020/03/30 Python
Django如何实现密码错误报错提醒
2020/09/04 Python
pycharm2020.1.2永久破解激活教程,实测有效
2020/10/29 Python
html5 canvas fillRect坐标和大小的问题解决方法
2014/03/26 HTML / CSS
英国领先的奢侈品零售商之一:CRUISE
2016/12/02 全球购物
介绍一下木马病毒的种类
2015/07/26 面试题
电子银行营销方案
2014/02/22 职场文书
汽车维修专业自荐书
2014/05/26 职场文书
大学新闻系自荐书
2014/05/31 职场文书
纺织工程专业推荐信
2014/09/08 职场文书
党委书记个人对照检查材料
2014/09/15 职场文书
破坏寝室公物检讨书
2014/11/17 职场文书
西柏坡观后感
2015/06/08 职场文书