高度自適應的輸入框
有時候我們需要一個高度能隨內容自動增加的輸入框,input
顯然不行,因為 input
裡的文字是不換行的。文字域 textarea
裡的文字倒是換行的,可一旦文字內容超過其高度,textarea
就會增加一個煩人的滾動條,這是很影響視覺的,就如同下面:
<textarea cols="30" rows="3"></textarea>
那麼有沒有辦法制作一個高度能隨文字內容自動增加的輸入框呢?答案是肯定的,下面介紹兩種方式。
方式一
這種方式依然使用 textarea
, 主要思想是我們將 textarea
放入一個容器中,同時在這個容器中放入一個隱藏的 div (visibility: hidden), 監聽 textarea
textarea
的高度為 100% 並將其定位到容器的左上角,那麼 textarea
的高度自然就是其中文字內容的高度了。
visibility
是一個CSS屬性,用來在不更改文件的佈局的前提下顯示或隱藏元素,它有三個可能的取值:
visible
元素正常顯示(預設值);hidden
隱藏元素,但是其他元素的佈局不改變,相當於此元素變成透明。
若將其子元素設為 visibility: visible,則該子元素依然可見;collapse
用於表格的行、列、列組和行組,隱藏表格的行或列,並且不佔用任何空間。
<!-- demo-1.html --> <div class="container"> <div></div> <textarea placeholder="輸入訊息..."></textarea> </div> <style> .container { width: 500px; position: relative; } .container div { visibility: hidden; /** 避免初始化時容器沒有高度 */ padding: 8px 0px; } .container textarea { width: 100%; height: 100%; position: absolute; top: 0px; padding: 0px; /** 必須設定為 content-box !!! */ box-sizing: content-box; } </style> <script> const textarea = document.querySelector('.container textarea'); const div = document.querySelector('.container div'); textarea.addEventListener('input', (e) => { div.innerText = e.target.value; }); </script>
檢視樣例:https://mxsyx.site/archives/10/#demo-1
你可能已經注意到了,當我們輸入文字時,輸入框的高度顯然要比文字內容高許多,伴隨著輸入文字的增多。高度差會越來越大,這是因為隱藏 div
與 文字域 textarea
內字型的尺寸與行高是不同的, div
內的字型尺寸與行高要比 textarea
內的大,所以 div
撐開的容器高度自然要高於 textarea
內的文字內容高度。要解決這個問題,統一它們的字型尺寸與行高就可以了。(注:div
的字型尺寸與行高預設繼承自父元素)
<!-- demo-1.html -->
<div class="container">
<div></div>
<textarea placeholder="輸入訊息..."></textarea>
</div>
<style>
.container {
width: 500px;
position: relative;
font-size: 14px;
line-height: 16px;
}
.container div {
visibility: hidden;
/** 避免初始化時容器沒有高度 */
padding: 8px 0px;
}
.container textarea {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
padding: 0px;
/** 必須設定為 content-box !!! */
box-sizing: content-box;
/** 設定字型尺寸與行高繼承自父元素 */
font-size: inherit;
line-height: inherit;
/* 去掉右下角的調整大小的標誌 */
resize: none;
}
</style>
<script>
const textarea = document.querySelector('.container textarea');
const div = document.querySelector('.container div');
textarea.addEventListener('input', (e) => {
div.innerText = e.target.value;
});
</script>
檢視樣例:https://mxsyx.site/archives/10/#demo-2
這樣一來高度就一致了。這種方式雖然可以較好的實現高度自適應的輸入框,但實現起來總感覺很粗糙,下面這種方式就明顯簡單多了。
方式二
像 div
, p
, blockquote
這樣的元素預設是不可編輯的,但我們可以將其 contenteditable
屬性設定為 true
, 使其變為可編輯的。
contenteditable
是一個全域性屬性,用於指示元素是否可被使用者編輯,該屬性必須採用以下值之一:
true
或者 '空字串', 表示該元素是可編輯的;false
, 表示該元素是不可編輯的。- 如果未設定此屬性,則其預設值將從其父元素繼承。
<div class="container" contenteditable="true"></div>
<style>
.container {
width: 500px;
font-size: 14px;
line-height: 16px;
border: solid 1px #999;
}
</style>
嘗試輸入一段文字吧:
是不是很簡單呢? 我們也可以使用CSS偽類 :empty, :focus, 實現placeholder
那樣的效果
<style>
.container {
width: 500px;
font-size: 14px;
line-height: 16px;
border: solid 1px #999;
}
.container:empty::before {
content: "輸入訊息...";
color: #999999;
}
.container:focus::before {
content: none;
}
</style>
嘗試輸入一段文字吧:
如果你使用 Vue.js, 我們也可以它封裝為一個Vue元件:
<template>
<div
class="msg-input"
contenteditable="true"
@input="changeText"
>{{ innerText }}</div>
</template>
<script>
export default{
name: "MsgInput",
props: ['value'],
data: function() {
return {
innerText: this.value,
}
},
methods: {
changeText() {
this.$emit('input', this.$el.innerText);
}
}
}
</script>
<style scoped>
.msg-input {
width: 500px;
font-size: 14px;
line-height: 16px;
border: solid 1px #999;
}
.msg-input:empty::before {
content: "輸入訊息...";
color: #999999;
}
.msg-input:focus::before {
content: none;
}
</style>
接下來在父元件中引用這個元件:
<template>
<div>
<MsgInput v-model="msg"/>
</div>
</template>
<script>
import MsgInput from "/MsgInput.vue";
export default {
name: "MsgToolkit",
data: function() {
return {
msg: ''
}
},
components: {
MsgInput
},
}
</script>
父元件為子元件使用 v-model
指令,將子元件的 value
與 父元件的 msg
雙向繫結在一起。當輸入事件發生後,子元件呼叫changeText方法,觸發一個 input
事件,父元件監聽到此事件,將事件傳遞過來的資料同步到 msg
上,由於資料是雙向繫結的,子元件的 value
值也會相應發生變化。更過原理請參考 自定義元件的-v-model
該篇部落格內的程式碼已同步到Github
參考資料:
[1]. MDN文件 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/contenteditable
[2]. MDN文件 https://developer.mozilla.org/zh-CN/docs/Web/CSS/visibility
[3]. MDN文件 https://developer.mozilla.org/zh-CN/docs/Web/CSS/:empty
[4]. MDN文件 https://developer.mozilla.org/zh-CN/docs/Web/CSS/:focus
[5]. Vue.js官方文件 https://cn.vuejs.org/v2/guide/components-custom-events.html#自定義元件的-v-mo