關於Element的textarea自適應高度
阿新 • • 發佈:2019-02-01
今天觀看學習Element的原始碼,看到textarea有一個自適應高度的屬性,畢竟以前也接觸過這方面的問題,你可以檢視此處:更強大的textarea高度自適應來了解我之前寫的一篇同樣是實現textarea自適應高度,所以就好奇看一下它是怎麼實現的。
先來看一下它的原始碼吧(各個階段大致的做的事情我已經標到程式碼上了):
let hiddenTextarea;
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
`;
// 所有可能會影響到height的css屬性
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'
];
// 獲取設定在當前textarea上的css屬性
function calculateNodeStyling(targetElement) {
const style = window.getComputedStyle(targetElement);
const boxSizing = style.getPropertyValue('box-sizing');
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 };
}
export default function calcTextareaHeight(
targetElement,
minRows = 1,
maxRows = null
) {
// 如果不存在就新建一個隱藏的textarea
if (!hiddenTextarea) {
hiddenTextarea = document.createElement('textarea');
document.body.appendChild(hiddenTextarea);
}
let {
paddingSize,
borderSize,
boxSizing,
contextStyle
} = calculateNodeStyling(targetElement);
// 將獲取到得當前得textarea的css屬性作用於隱藏的textarea
hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
// 將當前的textarea的value設定到隱藏的textarea上面
hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';
// 獲取隱藏的textarea的height
let height = hiddenTextarea.scrollHeight;
const result = {};
if (boxSizing === 'border-box') {
height = height + borderSize;
} else if (boxSizing === 'content-box') {
height = height - paddingSize;
}
hiddenTextarea.value = '';
let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
// 如果設定有最小行數和最大行數時的判斷條件
if (minRows !== null) {
let minHeight = singleRowHeight * minRows;
if (boxSizing === 'border-box') {
minHeight = minHeight + paddingSize + borderSize;
}
height = Math.max(minHeight, height);
result.minHeight = `${ minHeight }px`;
}
if (maxRows !== null) {
let maxHeight = singleRowHeight * maxRows;
if (boxSizing === 'border-box') {
maxHeight = maxHeight + paddingSize + borderSize;
}
height = Math.min(maxHeight, height);
}
// 將得到的height的高度設定到當前的textarea上面
result.height = `${ height }px`;
// 刪除掉無用的隱藏的textarea
hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
hiddenTextarea = null;
return result;
};
大致思路就是將當前textarea的所有可能會影響到height的css屬性全部設定給一個隱藏的textarea上,並且兩個的value一樣,再將隱藏的textarea的高度設定給當前的textarea,如果設定有最小和最大行數,則再做相應的事件。
不過這個是Element的一個檔案,我們一般情況下也沒辦法使用,所以我就想辦法把它修改為了一個依賴與jQuery的js檔案,使用的時候只需要直接引用這個js檔案就可以了,其他事件都不需要做,並且也支援設定最小和最大行數的。
下面放上一個例子:
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>textarea自適應高度</title>
<style>
textarea {
display: block;
resize: vertical;
padding: 5px 15px;
line-height: 1.5;
box-sizing: border-box;
width: 100%;
font-size: 14px;
color: #5a5e66;
background-color: #fff;
background-image: none;
border: 1px solid #d8dce5;
border-radius: 4px;
transition: border-color .2s cubic-bezier(.645,.045,.355,1);
margin-bottom: 20px;
}
</style>
</head>
<body>
<textarea name="text" cols="30" rows="1"></textarea>
<textarea name="text" cols="30" rows="2"></textarea>
<textarea name="text" cols="30" data-min-rows="2" data-max-rows="4"></textarea>
<script src="http://apps.bdimg.com/libs/jquery/1.11.3/jquery.js"></script>
<script src="autoheight-textarea.js"></script>
</body>
</html>
autoheight-textarea.js檔案:
$(function() {
var hiddenTextarea;
var HIDDEN_STYLE = `
height:0 !important;
visibility:hidden !important;
overflow:hidden !important;
position:absolute !important;
z-index:-1000 !important;
top:0 !important;
right:0 !important;
`;
// 所有可能會影響到height的css屬性
var 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'
];
// 獲取設定在當前textarea上的css屬性
function calculateNodeStyling(targetElement) {
var style = window.getComputedStyle(targetElement);
var boxSizing = style.getPropertyValue('box-sizing');
var paddingSize = (
parseFloat(style.getPropertyValue('padding-bottom')) +
parseFloat(style.getPropertyValue('padding-top'))
);
var borderSize = (
parseFloat(style.getPropertyValue('border-bottom-width')) +
parseFloat(style.getPropertyValue('border-top-width'))
);
var contextStyle = CONTEXT_STYLE
.map(function(value){
return value + ':' + style.getPropertyValue(value)
}).join(';');
return { contextStyle, paddingSize, borderSize, boxSizing };
}
$('body').on('focus', 'textarea', function () {
// 如果不存在就新建一個隱藏的textarea
var _this = $(this);
if (!hiddenTextarea) {
hiddenTextarea = document.createElement('textarea');
document.body.appendChild(hiddenTextarea);
}
var {
paddingSize,
borderSize,
boxSizing,
contextStyle
} = calculateNodeStyling(_this[0]);
// 將獲取到得當前得textarea的css屬性作用於隱藏的textarea
hiddenTextarea.setAttribute('style', HIDDEN_STYLE + contextStyle);
}).on('keydown keyup', 'textarea', function(){
var _this = $(this);
var {
paddingSize,
borderSize,
boxSizing,
contextStyle
} = calculateNodeStyling(_this[0]);
// 將獲取到得當前得textarea的css屬性作用於隱藏的textarea
hiddenTextarea.setAttribute('style', HIDDEN_STYLE + contextStyle);
// 將當前的textarea的value設定到隱藏的textarea上面
hiddenTextarea.value = _this[0].value || _this[0].placeholder || '';
// 獲取隱藏的textarea的height
var height = hiddenTextarea.scrollHeight;
if (boxSizing === 'border-box') {
height = height + borderSize;
} else if (boxSizing === 'content-box') {
height = height - paddingSize;
}
hiddenTextarea.value = '';
var singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
// 如果設定有最小行數和最大行數時的判斷條件,如果沒有設定則取rows為最小行數
var minRows = _this.attr('rows') ? _this.attr('rows') : _this.attr('data-min-rows') ? _this.attr('data-min-rows') : 1;
var maxRows = _this.attr('data-max-rows') ? _this.attr('data-max-rows') : null;
if (minRows !== null) {
var minHeight = singleRowHeight * minRows;
if (boxSizing === 'border-box') {
minHeight = minHeight + paddingSize + borderSize;
}
height = Math.max(minHeight, height);
}
if (maxRows !== null) {
var maxHeight = singleRowHeight * maxRows;
if (boxSizing === 'border-box') {
maxHeight = maxHeight + paddingSize + borderSize;
}
height = Math.min(maxHeight, height);
}
// 將得到的height的高度設定到當前的textarea上面
_this.css('height', height + 'px');
}).on('blur', 'textarea', function () {
// 刪除掉無用的隱藏的textarea
hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
hiddenTextarea = null;
})
})
Element的原始碼還是寫的很讚的,有時間多看看各種框架的原始碼。