DOM结构
html
//省略版
<div class="el-input-number">
<span class="el-input-number__decrease">
<i class="el-icon-minus"></i>
</span>
<span class="el-input-number__increase">
<i class="el-icon-plus"></i>
</span>
<div class="el-input-number__wrapper">
<el-input></el-input>
<span class="el-input-number__unit"></span>
</div>
</div>
组件的根元素是一个div,具有el-input-number类名。在这个div中,有两个子元素span,分别表示减少按钮和增加按钮。每个按钮中都包含一个i元素,用于显示减号和加号的图标。紧接着是一个div,具有el-input-number__wrapper类名,用于包裹输入框和单位元素。
html
<div class="el-input-number__wrapper">
<el-input
ref="input"
:value="displayValue"
:placeholder="placeholder"
:disabled="inputNumberDisabled"
:size="inputNumberSize"
:max="max"
:min="min"
:name="name"
:label="label"
@keydown.up.native.prevent="increase"
@keydown.down.native.prevent="decrease"
@blur="handleBlur"
@focus="handleFocus"
@input="handleInput"
@change="handleInputChange">
</el-input>
<span class="el-input-number__unit" ref="unit" v-if="controlsAtRight && unit">{{ unit }}</span>
</div>
在这个包裹元素中,有一个el-input组件,用于显示和输入数字。它具有一些属性,如value、placeholder、disabled、size、max、min、name、label等,以及一些事件监听器,如keydown.up.native.prevent、keydown.down.native.prevent、blur、focus、input、change等。最后,如果controlsAtRight为true且unit存在,则会在包裹元素中添加一个span元素,具有el-input-number__unit类名,用于显示单位。
mounted、updated
javascript
mounted() {
let innerInput = this.$refs.input.$refs.input;
innerInput.setAttribute('role', 'spinbutton');
innerInput.setAttribute('aria-valuemax', this.max);
innerInput.setAttribute('aria-valuemin', this.min);
innerInput.setAttribute('aria-valuenow', this.currentValue);
innerInput.setAttribute('aria-disabled', this.inputNumberDisabled);
if (this.controlsAtRight && this.unit) {
this.styleObj = { paddingRight: this.$refs.unit.clientWidth + this.$refs.button.clientWidth + 'px' };
} else if (this.controlsAtRight && !this.unit) {
this.styleObj = { paddingRight: 10 + this.$refs.button.clientWidth + 'px' };
} else {
this.styleObj = {};
}
},
updated() {
if (!this.$refs || !this.$refs.input) return;
const innerInput = this.$refs.input.$refs.input;
innerInput.setAttribute('aria-valuenow', this.currentValue);
}
组件的mounted和updated中对输入框进行了一些属性和样式的设置。 值得注意得是aria-
前缀的属性做什么的呢? aria 英文全称:Accessible Rich Internet Application ,翻译成中文就是:可访问的富互联网应用程序。其实它是一组属性,定义了使残疾人更容易访问 web 内容和 web 应用程序(尤其是使用JavaScript开发的应用程序)的方法。
computed
组件的computed中定义了一些计算属性,如minDisabled表示是否禁用减少按钮,maxDisabled表示是否禁用增加按钮,numPrecision表示数字的精度,controlsAtRight表示是否将增减按钮放在右侧,inputNumberSize表示输入框的尺寸,inputNumberDisabled表示输入框是否禁用,displayValue表示显示的值。
watch
- 组件的watch中监听了value和controlsAtRight的变化,当value变化时,会根据一些条件对新值进行处理,并将处理后的值赋给currentValue,然后触发input事件。
- 同时也监听了unit的变化,在代码中可以知道,当只有同时设置了controlsAtRight属性的时候,才会对styleObj进行样式的更改。另外还监听了styleObj属性,当styleObj变化时会动态设置input元素的样式。
data
javascript
data() {
return {
currentValue: 0,
userInput: null,
styleObj: {}
};
}
在组件的data中,定义了一些初始值,如currentValue表示当前的值,userInput表示用户输入的值,styleObj表示样式对象。
props
- step: 表示每次增加或减少的步长,默认为1。
- stepStrictly: 表示是否严格按照步长进行增减操作,默认为false。
- max: 表示允许的最大值,默认为Infinity。
- min: 表示允许的最小值,默认为-Infinity。
- value: 表示当前的值,可以是任意类型。
- disabled: 表示是否禁用输入框和按钮,默认为false。
- size: 表示输入框和按钮的尺寸,可以是字符串类型。
- unit: 表示输入框后面的单位文本,可以是字符串类型。
- controls: 表示是否显示增加和减少按钮,默认为true。
- controlsPosition: 表示增加和减少按钮的位置,默认为空字符串。
- name: 表示输入框的名称,可以是字符串类型。
- label: 表示输入框的标签文本,可以是字符串类型。
- placeholder: 表示输入框的占位符文本,可以是字符串类型。
- precision: 表示数字的精度,即小数点后的位数,必须是非负整数。
- 这些属性用于配置组件的行为和样式,父组件可以通过这些属性向子组件传递数据,并根据需要进行相应的操作。
methods
组件的methods中定义了一些方法,如toPrecision用于将数字保留指定的精度,getPrecision用于获取数字的精度,_increase和_decrease用于增加和减少数字,increase和decrease用于处理增加和减少按钮的点击事件,handleBlur和handleFocus用于处理输入框的失焦和聚焦事件,setCurrentValue用于设置当前的值,handleInput和handleInputChange用于处理输入框的输入事件,select用于选中输入框的内容。
- toPrecision(num, precision)
javascript
toPrecision(num, precision) {
if (precision === undefined) precision = this.numPrecision;
return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
}
在组件中,它被用于处理数字的精度,例如在计算增加或减少步长时,以及在显示值时进行精度处理。该方法接受两个参数:num表示要处理的数字,precision表示要保留的小数位数。如果precision参数未定义,则使用组件中的numPrecision属性作为默认精度。在方法内部,首先使用Math.pow函数计算出一个精度因子,即10的precision次方。然后将num乘以精度因子,再使用Math.round函数对结果进行四舍五入。最后,将结果除以精度因子,并使用parseFloat函数将其转换为浮点数类型
- getPrecision(value)
javascript
getPrecision(value) {
if (value === undefined) return 0;
const valueString = value.toString();
const dotPosition = valueString.indexOf('.');
let precision = 0;
if (dotPosition !== -1) {
precision = valueString.length - dotPosition - 1;
}
return precision;
}
这个函数的作用是根据给定的数字,判断其小数位数的精度。在组件中,它被用于计算步长和当前值的小数位数精度,以及在显示值时进行精度处理。在函数内部,首先进行了判断,如果value参数为undefined,则直接返回0,表示没有小数位数。接下来,将value转换为字符串类型,并使用indexOf方法查找小数点的位置。如果找到了小数点(即dotPosition不等于-1),则通过计算字符串长度减去小数点位置减1,得到小数位数的精度。
- _increase(val, step)
javascript
_increase(val, step) {
if (typeof val !== 'number' && val !== undefined) return this.currentValue;
const precisionFactor = Math.pow(10, this.numPrecision);
// Solve the accuracy problem of JS decimal calculation by converting the value to integer.
return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
}
在组件中,它被用于处理增加按钮的点击事件,根据步长和精度对当前值进行增加操作。在函数内部,首先进行了类型检查,如果val不是数字类型且不是undefined,则直接返回当前的值this.currentValue。接下来,定义了一个精度因子precisionFactor,它是通过将10的this.numPrecision次方计算得到的。然后,通过将val乘以精度因子,再加上step乘以精度因子,得到一个中间结果。这一步是为了解决JavaScript中小数计算的精度问题,通过将值转换为整数进行计算。最后,将中间结果除以精度因子,并使用this.toPrecision方法对结果进行精度处理,得到最终的增加后的值。
- _decrease(val, step)
javascript
_decrease(val, step) {
if (typeof val !== 'number' && val !== undefined) return this.currentValue;
const precisionFactor = Math.pow(10, this.numPrecision);
return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
}
这个函数与_increase()函数大同小异,唯一不同的是是个增加函数、一个是减少函数。这个则是减少函数。
- increase
javascript
increase() {
if (this.inputNumberDisabled || this.maxDisabled) return;
const value = this.value || 0;
const newVal = this._increase(value, this.step);
this.setCurrentValue(newVal);
}
在组件中,它被用于处理增加按钮的点击事件,根据步长和当前值进行增加操作,并更新显示的值。在函数内部,首先进行了条件判断,如果inputNumberDisabled为true(表示输入框被禁用)或者maxDisabled为true(表示已达到最大值),则直接返回,不进行增加操作。接下来,获取当前的值value,如果值为undefined,则将其设置为0。然后,调用_increase方法,将当前值value和步长step作为参数传递进去,得到一个新的值newVal。最后,调用setCurrentValue方法,将新的值newVal作为参数传递进去,用于更新当前的值。
- decrease
javascript
decrease() {
if (this.inputNumberDisabled || this.minDisabled) return;
const value = this.value || 0;
const newVal = this._decrease(value, this.step);
this.setCurrentValue(newVal);
}
这个函数与increase()函数大同小异,唯一不同的是是个点击增加事件、一个是点击减少事件。这个则是点击减少事件。
- setCurrentValue(value)
javascript
setCurrentValue(newVal) {
const oldVal = this.currentValue;
if (typeof newVal === 'number' && this.precision !== undefined) {
newVal = this.toPrecision(newVal, this.precision);
}
if (newVal >= this.max) newVal = this.max;
if (newVal <= this.min) newVal = this.min;
if (oldVal === newVal) return;
this.userInput = null;
this.$emit('input', newVal);
this.$emit('change', newVal, oldVal);
this.currentValue = newVal;
}
在组件中,它被用于处理用户输入、更新当前值,并触发input和change事件。在函数内部,首先保存旧的值oldVal。然后,进行判断,如果newVal的类型是数字,并且this.precision不为undefined,则调用toPrecision方法,将newVal按照指定的精度进行处理。接下来,进行一系列的判断和处理。如果newVal大于等于最大值this.max,则将newVal设置为最大值。如果newVal小于等于最小值this.min,则将newVal设置为最小值。然后,再次进行判断,如果旧的值oldVal等于新的值newVal,则直接返回,不进行后续操作。接下来,将this.userInput设置为null,表示用户输入为空。然后,通过$emit
方法触发input事件,将新的值newVal作为参数传递出去。接着,再次通过$emit
方法触发change事件,将新的值newVal和旧的值oldVal作为参数传递出去。最后,将当前的值this.currentValue设置为新的值newVal。
- handleInputChange(value)
javascript
handleInputChange(value) {
const newVal = value === '' ? undefined : Number(value);
if (!isNaN(newVal) || value === '') {
this.setCurrentValue(newVal);
}
this.userInput = null;
}
它被用于监听输入框的input事件,处理用户输入的值,并更新当前的值。首先根据输入的值value进行判断,如果值为空字符串'',则将newVal设置为undefined,否则将newVal设置为将输入的值转换为数字类型。接下来,进行一系列的判断。如果newVal不是NaN(即输入的值是一个有效的数字)或者值为空字符串'',则调用setCurrentValue方法,将newVal作为参数传递进去,用于更新当前的值。然后,将this.userInput设置为null,表示用户输入为空。
v-repeat-click
javascript
import { once, on } from 'element-ui/src/utils/dom';
export default {
bind(el, binding, vnode) {
let interval = null;
let startTime;
const handler = () => vnode.context[binding.expression].apply();
const clear = () => {
if (Date.now() - startTime < 100) {
handler();
}
clearInterval(interval);
interval = null;
};
on(el, 'mousedown', (e) => {
if (e.button !== 0) return;
startTime = Date.now();
once(document, 'mouseup', clear);
clearInterval(interval);
interval = setInterval(handler, 100);
});
}
};
在这段代码中,首先定义了一些变量,如interval用于存储定时器的ID,startTime用于记录鼠标按下的时间。然后定义了handler函数,它会调用指令绑定的表达式对应的方法。接下来定义了clear函数,它用于清除定时器,并根据鼠标按下的时间判断是否立即执行一次绑定的方法。在mousedown事件的回调函数中,首先判断鼠标按下的按钮是否为左键,如果不是则直接返回。然后记录鼠标按下的时间为startTime,并使用once函数绑定了mouseup事件,当鼠标松开时会执行clear函数。然后清除之前的定时器,再使用setInterval函数每隔100毫秒执行一次handler函数。