因为
有个需求,在input里面输入的文字可以自定义加粗改色等等,让人能一目了然的看见重点内容,比如hello,world!
1. div contenteditable=true
input[text]标签是无法实现输入的内容部分文字自定义样式的。- 如果要实现上述的样式,就需要一个可编辑的div,然后手动更改div的
innerHtml即可。- 把一个div做成可以编辑的输入框,
contenteditable是一个先决条件,设置为true,然后该div就有一定的编辑能力。
2. v-model的自定义组件内部实现
- 因为已经更改了文本内容,所以为了
可编辑div产生减少负面影响(bug),不能直接使用div,而是封装成一个组件。- 然后使用
v-model实现双向绑定value。(props:{value}和this.$emit('input',inputValue))- 既在组件内部更改文字样式呈现出来,
又在组件外部输出不带样式标签的实际真实内容,力求不影响页面的前端逻辑。
3. 组件实现
-
customInputDiv.vue
<template>
<div class="custom-input-div-wrap" @click="divClick">
<div class="custom-input-div" ref="cusInputRef" :contenteditable="true" @input="fieldInput" @focus="inputFocus" @blur="inputBlur" :placeholder="placeholder" @keydown="keydownClick">
</div>
</div>
</template>
<script>
import { isEmpty } from 'lodash';
export default {
name: "CustomInputDiv",
props: {
placeholder: {
type: String,
default: ''
},
value: { // 接受v-model中传过来的数据
type: String,
default: 'name: george,gender: male,class: 3,remark: i have adream'
},
},
watch: {
value: {
handler(newval) {
if(!newval) { // 监听传过来的数据如果为空,则清空本地div中的innerHTML
this.$refs.cusInputDivRef &&
(this.$refs.cusInputDivRef.innerHTML = "");
}
},
deep: true,
immediate: true
}
},
methods: {
keydownClick(e) { // 去除可编辑div的回车事件,以免光标换行,样式错乱
const event = e || window.event;
if(event.keyCode === 13) {
event.preventDefault
? event.preventDefault()
: (event.returnValue = false);
}
},
inputFocus () { //向父级组件触发focus 回调函数
this.$emit('cus-focus');
},
fieldInput () {
let text = this.$refs.cusInputDivRef.innerHTML;
let text = text
.replaceAll('<span style="color:red">', '')
.replaceAll('</span>', '')
.replaceAll('<span style="color:green">', '')
.replaceAll('</span>', '')
.replaceAll('<span style="color:blue">', '')
.replaceAll('</span>', '')
.replaceAll('<b>', '')
.replaceAll('</b>', '') // 在剪切/复制 再粘贴的过程中,可能会带有一个这种标签,需要被处理。
.replaceAll(' ', ' ');
this.$emit('input', text); // 组件内部更改部分样式呈现出来,组件外部数据依然是正确数据,相互不受影响
this.$emit('cus-input', text);
},
inputBlur() {
let colorList = ['red', 'green', 'blue'];
this.$refs.cusInputDivRef.innerHTML = this.value.split(',')
.map((v,i) => {
if(i < 3) {
const str = v.split(':')[0];
return v.replaceAll(str, (match) => `<span style="color:${colorList[i]}">${match}</span>`)
}
return v;
})
.join(',');//失去焦点后,更改文本样式,赋值给innerHTML
this.$refs.cusInputDivRef.scrollLeft = 0; // 文本靠左显示
this.$emit('cus-blur'); // 失焦回调。
},
clickClear() { // 给父级组件提供一个清空事件,方便清空,并回调。
this.$refs.cusInputDivRef.innerHTML = "";
this.$emit('click-clear');
},
divClick() { //点击div获取焦点
this.$refs.cusInputDivRef.focus();
}
}
}
</script>
<style lang="scss" scoped>
.custom-input-div-wrap {
width: 100%;
min-width: 100px;
padding: 10px 20px
border: 1px solid #000;
border-radius: 3px;
.custom-input-div {
background-color: transparent;
text-align:left;
height:40px;
line-height: 40px;
font-size: 14px;
color: #666;
outline:none;
box-sizing: border-box;
white-space: nowrap;
overflow-x:auto;
overflow-y:hidden;
&::-webkit-scrollbar {
display: none;
}
&:empty:before {
content: attr(placeholder);
color: #bdbdbd;
opacity: 1;
}
&:not(:empty):before {
content: none;
}
// &:focus:before {
// content: none;
// }
}
}
</style>
组件总结:
- 组件实现
v-model双向绑定,主要是设置 value的props 名[父级向子组件传输数据] 和this.$emit('input',inputValue)[子组件向父组件回传处理完的数据],形成一个双向绑定。- 因为可编辑div,按回车会换行,所以如果
不模拟textarea, 则废除回车的默认事件(preventDefault)- 在模拟输入框的时候,
可以使用css实现placeholder。
&:empty:before,使用div的伪元素,实现placeholder :表示当输入框为empty时,显示placeholder&:not(:empty):before: 表示当输入框不为empty时,不显示placeholder&:focus:before: 表示当输入框聚焦时,不显示placeholder- 具体使用啥,根据自己的需求而定。
-
view.vue
<template>
<div>
...
<customInputDiv ref="fieldDivRef" v-model="inputText" @cus-focus="inputFocus" @cus-blur="inputBlur" @click-clear="clickClear" @cus-input="fieldInput" :placeholder="placeholder">
</customInputDiv>
<button @click="clearText">清空内容</button>
</div>
...
</template>
<script>
import customInputDiv from './customInputDiv.vue';
export default {
...
data() {
return {
inputText: '',
placeholder: 'enter please'
}
},
components: {
customInputDiv:customInputDiv
},
...
methods: {
inputFocus() {}, // 聚焦回调
inputBlur() {}, // 失焦回调
clickClear() {}, // 清空回调
fieldInput() {}, // input 回调
clearText() {
this.$refs.fieldDivRef.clickClear(); // 清空输入框的文字
}
}
}
</script>
...