高度自适应输入组件实践
TIP
最近在业务开发需要制作一些表单表格页面,由于场景需求无法用到编译时的能力,且其中一些地方要求实现支持多行输入并且高度自适应的文本框,在经过一些社区文章翻阅后,决定使用contenteditable
来做这个封装
一些对比
Textarea
textarea天然对多行文本输入有着良好的支持,还有对行数,最大字符等属性支持以及Vue绑定语法糖的支持。但由于其高度无法自适应,需要借助scrollHeight做监听支持,页面初始化时的字符计算等
Ep-Input
ElementPlus的Input组件支持多行输入模式,没有细看其中的实现,大概理解为是通过字符数计算动态改变了组件高度(不一定准确), 但页面是仅基于Vue.js
进行手动封装, r没有引入Ep组件库,放弃该方案
Contenteditable
NOTE
该属性是富文本支持,在需要仅文本输入的情况下应该将属性值设置plaintext-only
contenteditable 属性可以让元素天然支持富文本输入,且元素由于文本改变在最大宽度限制下可以自动撑开内容,在不需要过多的表单组件属性支持时,使用contenteditable非常简单,需要做的仅仅是将其变为支持双向绑定的输入组件
组件实现
代码实现是基于非SFC组件下使用Vue框架
选项式
js
const { h } = Vue
const childAreaInput = {
data() {
return {
msg:this.modelValue
}
},
props:{
modelValue:{
type:String,
default:''
}
},
watch: {
// 监听modelValue的变化,同步更新msg
modelValue(newVal) {
this.msg = newVal;
}
},
methods: {
// 更新变量值
AreaInput() {
this.$emit('update:modelValue', this.$refs.AreaInput.textContent);
},
},
render() {
return h('div',
{
contenteditable: true,
onInput: this.AreaInput,
ref: 'AreaInput',
style:{
whiteSpace:'pre-wrap',
width:'100%',
}
},
this.msg)
}
}
组合式
js
const {defineComponent,ref,watch,h} = Vue
const useAreaInput = (props,emit) => {
const msg = ref(props.modelValue)
const areaInput = (event) => {
emit('update:modelValue',event.target.textContent)
}
watch(()=>props.modelValue,(newValue)=>{
msg.value = newValue
})
return {msg,areaInput,submitData}
}
const childAreaInputByComposable = defineComponent({
props:{
modelValue:{
type:String,
default:''
}
},
emits:['update:modelValue'],
setup(props,{emit}){
const { msg, areaInput } = useAreaInput(props, emit);
return ()=>{
const { h } = Vue
return h('div',
{
contenteditable: true,
onInput: areaInput,
ref: 'AreaInput',
style:{
whiteSpace:'pre-wrap',
width:'100%',
}
},
msg)
}
}
})
使用
js
const MainApp = {
el: "#app",
components: {
AreaInput: childAreaInput,
AreaInputByComposable:childAreaInputByComposable
}
}
// 实例化
const app = Vue.createApp(OQC)
// 挂载
app.mount("#app")
html
<Area-Input v-model="mainData.custom" />
<Area-Input-By-Composable v-model="mainData.custom"/>