vue文本编辑器
特性:
支持placeholder
支持与fastclick插件共存,如果不需要,可以去掉needsclick这个class
支持v-model
支持readonly
支持任意数量个实例
支持移动端换行
支持最大字符限制
ios和mac下没有点击bug
直接上代码:
div-editor.vue
<template>
<p
:id="`divEditor_${id}`"
:ref="`divEditor_${id}`"
:placeholder="placeholder"
class="div-editor needsclick"
:class="{ readonly: readonly }"
@blur="focus"
@focus="focus"
@input="update"
@click="editContent"
v-html="valueText"
:contenteditable="!readonly"
></p>
</template>
<script>
import uuid from "@/util/uuid";
export default {
model: {
prop: "value",
event: "change",
},
props: {
value: {
type: String,
default: "",
},
placeholder: {
type: String,
default: "请输入",
},
readonly: {
type: Boolean,
default: false,
},
maxLength: {
type: Number,
default: null,
},
},
data() {
return {
id: uuid(8, 2),
focusIn: false,
valueText: "",
};
},
created() {
this.valueText = this.value;
},
watch: {
value(newVal) {
if (!this.focusIn) {
this.valueText = newVal;
}
},
},
methods: {
editContent() {
if (this.readonly) {
return;
}
let el = this.$refs[`divEditor_${this.id}`];
if (this.valueText.length) {
let sel = window.getSelection();
sel.collapse(el, 1);
}
el.focus();
this.focusIn = true;
},
update(e) {
if (this.maxLength && e.target.innerHTML.length > this.maxLength) {
let text = e.target.innerHTML.slice(0, this.maxLength);
text = text.replace(/(<br>)|(&|<)|([&<?])$/gi, "");
e.target.innerHTML = text;
this.$emit("change", text);
this.setFocus(e);
} else {
if (!this.readonly) {
this.$emit("change", e.target.innerHTML);
}
}
},
focus(e) {
this.focusIn = true;
},
blur() {
this.focusIn = false;
},
setFocus() {
let obj = this.$refs[`divEditor_${this.id}`];
if (window.getSelection) {
//ie11 10 9 ff safari
var range = window.getSelection(); //创建range
range.selectAllChildren(obj); //range 选择obj下所有子内容
range.collapseToEnd(); //光标移至最后
} else if (document.selection) {
//ie10 9 8 7 6 5
var range = document.selection.createRange(); //创建选择对象
range.moveToElementText(obj); //range定位到obj
range.collapse(false); //光标移至最后
range.select();
}
},
},
};
</script>
<style lang="scss" scoped>
.div-editor {
width: 100%;
outline: 0 none;
min-height: 45px;
max-height: 300px;
white-space: pre;
word-break: break-all;
background-color: transparent;
border: 0 none;
font-size: 32px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 45px;
text-align: left;
overflow-y: auto;
&[contenteditable="true"] {
margin: 0;
padding: 0;
-webkit-user-select: text;
user-select: text;
-webkit-user-modify: read-write-plaintext-only;
* {
-webkit-user-select: text !important;
user-select: text !important;
}
}
&:empty::after {
content: attr(placeholder);
font-size: 30px;
font-family: PingFangSC-Regular, PingFang SC;
line-height: 45px;
color: rgba(0, 0, 0, 0.5);
text-align: left;
pointer-events: none;
}
&.readonly {
&:empty::after {
display: none;
}
}
}
</style>