element文字域選擇框_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) {undefined
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 // 最大的行數
) {undefined
if (!hiddenTextarea) { // 來建立一個隱藏的文字域,所有的計算都是在這上面進行的
hiddenTextarea = document.createElement('textarea');
document.body.appendChild(hiddenTextarea);
}
// 獲取結點一些樣式值
let {undefined
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') {undefined
// 如果是 border-box,說明高度得加上邊框
height = height + borderSize;
} else if (boxSizing === 'content-box') {undefined
// 如果是 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() {undefined
this.$on('inputSelect', this.inputSelect);
},
inputSelect方法會呼叫refs上的input的原生的select方法,來選中該input。
methods: {undefined
inputSelect() {undefined
this.$refs.input.select();
},
}
mounted
掛載的時候,會呼叫resizeTextarea方法來設定文字域的大小。
mounted() {undefined
this.resizeTextarea();
}
methods: {undefined
resizeTextarea() {undefined
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。
type
type是一個prop,它預設設定為text,如果設定為textarea,表明當前是一個文字域。
props: {undefined
type: {undefined
type: String,
default: 'text'
},
}
size
size也是一個prop,用來設定輸入框的大小,在textarea下無效。
props: {undefined
size: String,
}
disabled
disabled也是一個prop,用來設定是否可用。
props: {undefined
disabled: Boolean,
}
prepend、append
這兩個都是在設定輸入框組的時候使用的,通過具名slot傳入,分別放置於input的首和尾。
input
然後,根據type的不同使用v-if分別渲染input或者textarea,我們先分析input部分。
前置元素
前置元素直接通過具名slot傳入。
input 圖示
圖示也是通過具名slot傳入的,也可以通過prop中的icon傳入圖示名。
class="el-input__icon"
:class="'el-icon-' + icon"
v-if="icon"
@click="handleIconClick">
上面還綁定了一個handleIconClick的點選事件,它會觸發click事件:
methods: {undefined
handleIconClick(event) {undefined
this.$emit('click', event);
},
}
input
然後是最重要的input部分,上面大部分是prop,不進行講解,其餘的我們將一一講解。
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: {undefined
'value'(val, oldValue) {undefined
this.setCurrentValue(val);
}
},
而setCurrentValue是用來改變當前值的。
methods: {undefined
setCurrentValue(value) {undefined
if (value === this.currentValue) return; // 如果新舊值一致直接返回
this.$nextTick(_ => {undefined
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: {undefined
handleInput(event) {undefined
this.setCurrentValue(event.target.value); // 改變當前值
},
}
handleFocus
handleFocus用來處理獲得焦點的事件,會直接觸發focus事件。
methods: {undefined
handleFocus(event) {undefined
this.$emit('focus', event);
},
}
handleBlur
handleBlur用來處理失去焦點的事件。
methods: {undefined
handleBlur(event) {undefined
this.$emit('blur', event); // 觸發 blur 事件
this.dispatch('ElFormItem', 'el.form.blur', [this.currentValue]); // 向父元件派發 el.form.blur 事件
},
}
loading
loading會根據計算屬性validating來決定是否渲染。
computed: {undefined
validating() {undefined
return this.$parent.validateState === 'validating';
}
},
後置元素
後置元素只能根據具名slot傳入。
Textarea
如果type設定為textarea則會渲染textarea,上面繫結的都和input類似,不再多說,多了一個textareaStyle,是根據calcTextareaHeight計算出來的。
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">
————————————————
版權宣告:本文為CSDN博主「weixin_39533896」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/weixin_39533896/article/details/111516689