vue实现div可编辑,并插入指定元素,样式
前言:
vue中实现一个既可以编辑内容,有可以动态编辑内容插入一个带有样式的内容,改变默认内容后,这个样式消失的效果,这里来整理下调研与解决实现问题之路。
实现最终效果:图2为默认内容
1、可以光标点击任意地方,然后点击按钮,插入带有span的内容
2、默认span是有样式,但是一旦内容与我们的默认内容不同就取消样式
3、获取的时候,可以拿到带有标签的内容,也可以拿到纯文本内容
4、默认内容,支持带标签内容
探索之路:
1、刚开始从网上查找了很多资料,但是都有不同的缺陷,下面是我根据网上资料实现的效果
1)封装一个 inputDiv.vue
-
<template>
-
<div
-
ref="divInput"
-
class="edit-div"
-
style="-webkit-user-select: auto"
-
:contenteditable="canEdit"
-
@blur="onBlur"
-
@input="changeText($event)"
-
@keyup.space="changeSpace($event)"
-
@keyup.enter="changeEnter($event)"
-
@paste="onPaste"
-
@focus="onFocus"
-
placeholder="请输入消息内容"
-
slot="title"
-
v-html="innerText"
-
></div>
-
-
-
</template>
-
<script>
-
export default {
-
props: {
-
value: {
-
type: String,
-
default: "",
-
},
-
canEdit: {
-
type: Boolean,
-
default: true,
-
},
-
},
-
data() {
-
return {
-
innerText: this.value,
-
isLocked: false,
-
}
-
},
-
watch: {
-
value() {
-
if (!this.isLocked && !this.innerHTML) {
-
this.innerText = this.value
-
}
-
},
-
},
-
methods: {
-
getRefBlur() {
-
this.$refs.divInput.blur()
-
},
-
getRefFocus() {
-
this.$refs.divInput.focus()
-
},
-
onBlur() {
-
this.$emit("onblurChange", this.$el.innerHTML)
-
this.isLocked = false
-
},
-
// 主要用于处理光标得定位问题
-
keepLastIndex(obj) {
-
console.log(obj);
-
if (window.getSelection) {
-
obj.focus()
-
let range = window.getSelection()
-
range.selectAllChildren(obj)
-
range.collapseToEnd()
-
} else if (document.selection) {
-
let range = document.selection.createRange() //创建选择对象
-
range.moveToElementText(obj) //range定位到obj
-
range.collapse(false) //光标移至最后
-
range.select()
-
}
-
},
-
changeSpace(e) {
-
setTimeout(() => {
-
this.keepLastIndex(e.target)
-
}, 30)
-
this.$emit("keyupSpace", this.$el.innerHTML)
-
},
-
changeEnter(e) {
-
setTimeout(() => {
-
this.keepLastIndex(e.target)
-
}, 30)
-
this.$emit("keyupEnter", this.$el.innerHTML)
-
},
-
// 实时监听当前内容
-
changeText(e) {
-
console.log(e)
-
if(e.data){
-
console.log('数据:' this.$el.innerHTML)
-
setTimeout(() => {
-
this.keepLastIndex(e.target)
-
}, 30)
-
this.$emit("input", this.$el.innerHTML)
-
}
-
},
-
// 输入框粘贴事件
-
onPaste(event) {
-
this.$emit("onPaste", event)
-
},
-
//
-
onFocus(event) {
-
this.$emit("onFocus", event)
-
},
-
}
-
}
-
</script>
-
-
<style scoped>
-
-
</style>
2)父级中使用:
-
结果:{{editInputVal}}-{{mrEditInputVal}}
-
<el-button @click = 'addStrBtnFun'>{{addBtnText}}</el-button>
-
<inputDiv
-
ref="imitate"
-
id="imitate"
-
class="imitate-input imitate-placeholder js-imitate-input"
-
@onblurChange="onblurChange($event)"
-
@onFocus="getFocus"
-
@onPaste="onPaste"
-
v-html="mrEditInputVal"
-
@input="contentedInput($event)"
-
@keyupSpace="contentedKeySpace($event)"
-
@keyupEnter="contentedKeyEnter($event)"
-
@changeKeyup="changeKeyup($event)"
-
></inputDiv>
-
js部分:
-
-
import inputDiv from './components/inputDiv'
-
components: {
-
inputDiv,
-
},
-
data() {
-
return {
-
mrEditInputVal:'',
-
editInputVal:'',
-
addBtnText:'插入员工昵称',
-
abc:'',
-
teamContent: "<p>幸福 人和 测试增加数据 在加1111222</p>"
-
}
-
},
-
-
methods: {
-
onblurChange(val){
-
// debugger
-
},
-
getFocus(val){
-
// debugger
-
},
-
onPaste(val){
-
// debugger
-
},
-
contentedInput(val){
-
if(val.indexOf('<span')!= -1){
-
let arr1 = val.split('<span')
-
let arr2 = arr1[1].split('/span>')
-
let arr3 = arr2[0].split('>')
-
let arr4 = arr3[1].split('<')
-
if(arr4[0] != this.addBtnText){
-
let val = this.editInputVal = arr1[0] arr4[0] arr2[1]
-
this.mrEditInputVal = JSON.parse(JSON.stringify(val))
-
return
-
}
-
}
-
this.editInputVal = val
-
},
-
contentedKeySpace(val){
-
// debugger
-
},
-
contentedKeyEnter(val){
-
// debugger
-
},
-
changeKeyup(val){
-
// debugger
-
},
-
addStrBtnFun(){
-
let index = this.getCursorPosition()
-
let insertStr = (soure,start, newStr) => {
-
return soure.slice(0, start) newStr soure.slice(start)
-
}
-
let newStr = insertStr(this.editInputVal,index,'<span class="selColor">' this.addBtnText '</span>')
-
this.mrEditInputVal = newStr
-
},
-
getCursorPosition(){
-
const selection = window.getSelection();
-
const range = selection.getRangeAt(0);
-
const cursorPos = range.startOffset;
-
return cursorPos
-
},
3)最终实现效果
1、实现了不同内容的时候可以正常监听
2、点击按钮,可以正常插入并修改样式
缺点:
1、点击多个插入,位置有问题
2、每次输入第二个字符的时候,光标都移动到了最后一位
2、继续努力实现效果,效果图实现步骤:
注意:
不能实时获取它的数据,它的光标位置会出现问题,目前官方提供的方法,默认是回到第一个,可以修改到回到最后一个,但是我们如果是中间输入内容,光标会异常,故而不建议实时获取数据
1、div contenteditable="true" 来实现div的可编辑,v-html来实现对他默认值的修改
-
<div
-
class="edit"
-
contenteditable="true"
-
@input="editInput"
-
placeholder="请输入消息内容"
-
v-html="innerHtml"
-
>
-
</div>
2、初始化方法
// 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。
-
initEditFun(){
-
editDiv = document.getElementsByClassName("edit")[0]
-
-
editDiv.addEventListener("blur", () => {
-
// 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。
-
range = window.getSelection().getRangeAt(0)
-
})
-
-
},
3、点击按钮时候,给可编辑区域中增加一个带有颜色的span
记得在你的功能样式中添加,可在app.vue,也可以在你项目其他公共区域
-
.selColor{
-
color: red;
-
}
点击事件:
window.getSelection() 获取他的光标选取
selection.selectAllChildren(editDiv) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
selection.collapseToEnd() //Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。(想查看更多属性,点击链接,去看官方api)
range = window.getSelection().getRangeAt(0) // 保存当前编辑框的选区对象
let sel = window.getSelection()
range.insertNode(span) // insertNode方法,在range选区开头插入一个节点sel.removeAllRanges() //removeAllRanges方法:删除之前的所有选区。
sel.addRange(range) // 这一步就是添加当前区域对象到选区对象中
-
addStrBtnFun(){
-
const span = document.createElement("span")
-
span.innerText = this.addBtnText
-
span.className = 'selColor'
-
// 如果在页面刷新再点击编辑框之前就点击了按钮,此时range中并没有选区范围对象
-
if (range === "") {
-
let selection = window.getSelection()
-
selection.selectAllChildren(editDiv) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
-
/*
-
Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。
-
以上selectAllChildren方法,将div中子节点全部选中,collapseToEnd方法将选中区域取消,并且将光标定位到原区的末尾。
-
*/
-
selection.collapseToEnd()
-
range = window.getSelection().getRangeAt(0) // 无论哪一步都需要保存当前编辑框的选区对象
-
}
-
let sel = window.getSelection()
-
range.insertNode(span) // insertNode方法,在range选区开头插入一个节点
-
/*
-
removeAllRanges方法:删除之前的所有选区。
-
这一步的意义:因为当我们点击其他区域时,选区对象已经改变,不再是编辑框中的选区对象,这时候我们接下来的操作都不会符合我们想象中的样子
-
*/
-
sel.removeAllRanges()
-
sel.addRange(range) // 这一步就是添加当前区域对象到选区对象中,所以选区对象会再次指向编辑框中的选区,不会出现别的错误操作。
-
sel.collapseToEnd()
-
},
4、我们修改span内容,让他的样式消失方法
1)首先要用到 他的@input事件
@input="editInput"
-
editInput(e) {
-
console.log(e);
-
console.log(e.target.children);
-
},
这是它的默认拿到的数据
如果插入span以后,可以看到e.target.children多了span信息
拿我们页面的edit div下面所有的span,如果哪个内容被改变,就去掉它的class
-
editInput(e) {
-
console.log(e);
-
console.log(e.target.children);
-
if(e.target.children.length>0){
-
this.editSpanClass()
-
}
-
},
-
editSpanClass(){
-
let spanAll = document.querySelectorAll('.edit span')
-
spanAll.forEach(item=>{
-
if(item.innerHTML != this.addBtnText){
-
item.className = ''
-
}
-
})
-
},
源码分享:tinymce.vue
-
<template>
-
<div id="editDivBody">
-
<el-button @click = 'addStrBtnFun'>{{addBtnText}}</el-button>
-
<el-button @click = 'getNowContent'>获取当前内容</el-button>
-
<div
-
class="edit"
-
contenteditable="true"
-
@input="editInput"
-
placeholder="请输入消息内容"
-
v-html="innerHtml"
-
>
-
</div>
-
-
{{innerHtml}}
-
<hr>
-
{{innerText}}
-
</div>
-
</template>
-
-
<script>
-
let editDiv = null //编辑元素
-
let range = "" //选区
-
export default {
-
data () {
-
return {
-
addBtnText:'插入员工昵称',
-
// 赋值
-
innerHtml:'haoxing<h1>111</h1>',
-
innerText:'',
-
// 获取当前最新数据
-
getDataTime:1000,
-
getDataTimeInter:null,
-
}
-
},
-
mounted() {
-
this.initEditFun()
-
},
-
methods:{
-
initEditFun(){
-
editDiv = document.getElementsByClassName("edit")[0]
-
-
editDiv.addEventListener("blur", () => {
-
// 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。
-
range = window.getSelection().getRangeAt(0)
-
})
-
-
},
-
editInput(e) {
-
console.log(e);
-
console.log(e.target.children);
-
if(e.target.children.length>0){
-
this.editSpanClass()
-
}
-
},
-
editSpanClass(){
-
let spanAll = document.querySelectorAll('.edit span')
-
spanAll.forEach(item=>{
-
if(item.innerHTML != this.addBtnText){
-
item.className = ''
-
}
-
})
-
},
-
addStrBtnFun(){
-
const span = document.createElement("span")
-
span.innerText = this.addBtnText
-
span.className = 'selColor'
-
// 如果在页面刷新再点击编辑框之前就点击了按钮,此时range中并没有选区范围对象
-
if (range === "") {
-
let selection = window.getSelection()
-
selection.selectAllChildren(editDiv) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
-
/*
-
Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。
-
以上selectAllChildren方法,将div中子节点全部选中,collapseToEnd方法将选中区域取消,并且将光标定位到原区的末尾。
-
*/
-
selection.collapseToEnd()
-
range = window.getSelection().getRangeAt(0) // 无论哪一步都需要保存当前编辑框的选区对象
-
}
-
let sel = window.getSelection()
-
range.insertNode(span) // insertNode方法,在range选区开头插入一个节点
-
/*
-
removeAllRanges方法:删除之前的所有选区。
-
这一步的意义:因为当我们点击其他区域时,选区对象已经改变,不再是编辑框中的选区对象,这时候我们接下来的操作都不会符合我们想象中的样子
-
*/
-
sel.removeAllRanges()
-
sel.addRange(range) // 这一步就是添加当前区域对象到选区对象中,所以选区对象会再次指向编辑框中的选区,不会出现别的错误操作。
-
sel.collapseToEnd()
-
},
-
// 获取最新的内容
-
getNowContent(){
-
let editDiv = document.querySelectorAll('.edit')
-
this.innerHtml = editDiv[0].innerHTML
-
this.innerText = editDiv[0].innerText
-
}
-
-
}
-
}
-
</script>
-
<style scoped>
-
.edit{
-
width: 400px;
-
height:200px;
-
border:1px solid #ccc;
-
}
-
.edit:focus {
-
outline: 0;
-
border-color: #409EFF;
-
}
-
</style>
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfjaahj
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13