• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

vue实现div可编辑,并插入指定元素,样式

武飞扬头像
妍崽崽@
帮助1

前言:

        vue中实现一个既可以编辑内容,有可以动态编辑内容插入一个带有样式的内容,改变默认内容后,这个样式消失的效果,这里来整理下调研与解决实现问题之路。

实现最终效果:图2为默认内容

1、可以光标点击任意地方,然后点击按钮,插入带有span的内容

2、默认span是有样式,但是一旦内容与我们的默认内容不同就取消样式

3、获取的时候,可以拿到带有标签的内容,也可以拿到纯文本内容

4、默认内容,支持带标签内容

学新通

 学新通

 探索之路:

1、刚开始从网上查找了很多资料,但是都有不同的缺陷,下面是我根据网上资料实现的效果

1)封装一个  inputDiv.vue

  1.  
    <template>
  2.  
    <div
  3.  
    ref="divInput"
  4.  
    class="edit-div"
  5.  
    style="-webkit-user-select: auto"
  6.  
    :contenteditable="canEdit"
  7.  
    @blur="onBlur"
  8.  
    @input="changeText($event)"
  9.  
    @keyup.space="changeSpace($event)"
  10.  
    @keyup.enter="changeEnter($event)"
  11.  
    @paste="onPaste"
  12.  
    @focus="onFocus"
  13.  
    placeholder="请输入消息内容"
  14.  
    slot="title"
  15.  
    v-html="innerText"
  16.  
    ></div>
  17.  
     
  18.  
     
  19.  
    </template>
  20.  
    <script>
  21.  
    export default {
  22.  
    props: {
  23.  
    value: {
  24.  
    type: String,
  25.  
    default: "",
  26.  
    },
  27.  
    canEdit: {
  28.  
    type: Boolean,
  29.  
    default: true,
  30.  
    },
  31.  
    },
  32.  
    data() {
  33.  
    return {
  34.  
    innerText: this.value,
  35.  
    isLocked: false,
  36.  
    }
  37.  
    },
  38.  
    watch: {
  39.  
    value() {
  40.  
    if (!this.isLocked && !this.innerHTML) {
  41.  
    this.innerText = this.value
  42.  
    }
  43.  
    },
  44.  
    },
  45.  
    methods: {
  46.  
    getRefBlur() {
  47.  
    this.$refs.divInput.blur()
  48.  
    },
  49.  
    getRefFocus() {
  50.  
    this.$refs.divInput.focus()
  51.  
    },
  52.  
    onBlur() {
  53.  
    this.$emit("onblurChange", this.$el.innerHTML)
  54.  
    this.isLocked = false
  55.  
    },
  56.  
    // 主要用于处理光标得定位问题
  57.  
    keepLastIndex(obj) {
  58.  
    console.log(obj);
  59.  
    if (window.getSelection) {
  60.  
    obj.focus()
  61.  
    let range = window.getSelection()
  62.  
    range.selectAllChildren(obj)
  63.  
    range.collapseToEnd()
  64.  
    } else if (document.selection) {
  65.  
    let range = document.selection.createRange() //创建选择对象
  66.  
    range.moveToElementText(obj) //range定位到obj
  67.  
    range.collapse(false) //光标移至最后
  68.  
    range.select()
  69.  
    }
  70.  
    },
  71.  
    changeSpace(e) {
  72.  
    setTimeout(() => {
  73.  
    this.keepLastIndex(e.target)
  74.  
    }, 30)
  75.  
    this.$emit("keyupSpace", this.$el.innerHTML)
  76.  
    },
  77.  
    changeEnter(e) {
  78.  
    setTimeout(() => {
  79.  
    this.keepLastIndex(e.target)
  80.  
    }, 30)
  81.  
    this.$emit("keyupEnter", this.$el.innerHTML)
  82.  
    },
  83.  
    // 实时监听当前内容
  84.  
    changeText(e) {
  85.  
    console.log(e)
  86.  
    if(e.data){
  87.  
    console.log('数据:' this.$el.innerHTML)
  88.  
    setTimeout(() => {
  89.  
    this.keepLastIndex(e.target)
  90.  
    }, 30)
  91.  
    this.$emit("input", this.$el.innerHTML)
  92.  
    }
  93.  
    },
  94.  
    // 输入框粘贴事件
  95.  
    onPaste(event) {
  96.  
    this.$emit("onPaste", event)
  97.  
    },
  98.  
    //
  99.  
    onFocus(event) {
  100.  
    this.$emit("onFocus", event)
  101.  
    },
  102.  
    }
  103.  
    }
  104.  
    </script>
  105.  
     
  106.  
    <style scoped>
  107.  
     
  108.  
    </style>

2)父级中使用:

  1.  
    结果:{{editInputVal}}-{{mrEditInputVal}}
  2.  
    <el-button @click = 'addStrBtnFun'>{{addBtnText}}</el-button>
  3.  
    <inputDiv
  4.  
    ref="imitate"
  5.  
    id="imitate"
  6.  
    class="imitate-input imitate-placeholder js-imitate-input"
  7.  
    @onblurChange="onblurChange($event)"
  8.  
    @onFocus="getFocus"
  9.  
    @onPaste="onPaste"
  10.  
    v-html="mrEditInputVal"
  11.  
    @input="contentedInput($event)"
  12.  
    @keyupSpace="contentedKeySpace($event)"
  13.  
    @keyupEnter="contentedKeyEnter($event)"
  14.  
    @changeKeyup="changeKeyup($event)"
  15.  
    ></inputDiv>
  1.  
    js部分:
  2.  
     
  3.  
    import inputDiv from './components/inputDiv'
  4.  
    components: {
  5.  
    inputDiv,
  6.  
    },
  7.  
    data() {
  8.  
    return {
  9.  
    mrEditInputVal:'',
  10.  
    editInputVal:'',
  11.  
    addBtnText:'插入员工昵称',
  12.  
    abc:'',
  13.  
    teamContent: "<p>幸福 人和 测试增加数据 在加1111222</p>"
  14.  
    }
  15.  
    },
  16.  
     
  17.  
    methods: {
  18.  
    onblurChange(val){
  19.  
    // debugger
  20.  
    },
  21.  
    getFocus(val){
  22.  
    // debugger
  23.  
    },
  24.  
    onPaste(val){
  25.  
    // debugger
  26.  
    },
  27.  
    contentedInput(val){
  28.  
    if(val.indexOf('<span')!= -1){
  29.  
    let arr1 = val.split('<span')
  30.  
    let arr2 = arr1[1].split('/span>')
  31.  
    let arr3 = arr2[0].split('>')
  32.  
    let arr4 = arr3[1].split('<')
  33.  
    if(arr4[0] != this.addBtnText){
  34.  
    let val = this.editInputVal = arr1[0] arr4[0] arr2[1]
  35.  
    this.mrEditInputVal = JSON.parse(JSON.stringify(val))
  36.  
    return
  37.  
    }
  38.  
    }
  39.  
    this.editInputVal = val
  40.  
    },
  41.  
    contentedKeySpace(val){
  42.  
    // debugger
  43.  
    },
  44.  
    contentedKeyEnter(val){
  45.  
    // debugger
  46.  
    },
  47.  
    changeKeyup(val){
  48.  
    // debugger
  49.  
    },
  50.  
    addStrBtnFun(){
  51.  
    let index = this.getCursorPosition()
  52.  
    let insertStr = (soure,start, newStr) => {
  53.  
    return soure.slice(0, start) newStr soure.slice(start)
  54.  
    }
  55.  
    let newStr = insertStr(this.editInputVal,index,'<span class="selColor">' this.addBtnText '</span>')
  56.  
    this.mrEditInputVal = newStr
  57.  
    },
  58.  
    getCursorPosition(){
  59.  
    const selection = window.getSelection();
  60.  
    const range = selection.getRangeAt(0);
  61.  
    const cursorPos = range.startOffset;
  62.  
    return cursorPos
  63.  
    },

3)最终实现效果

1、实现了不同内容的时候可以正常监听

2、点击按钮,可以正常插入并修改样式

缺点:

1、点击多个插入,位置有问题

2、每次输入第二个字符的时候,光标都移动到了最后一位

学新通

 2、继续努力实现效果,效果图实现步骤:

注意:

        不能实时获取它的数据,它的光标位置会出现问题,目前官方提供的方法,默认是回到第一个,可以修改到回到最后一个,但是我们如果是中间输入内容,光标会异常,故而不建议实时获取数据

1、div contenteditable="true"  来实现div的可编辑,v-html来实现对他默认值的修改

  1.  
    <div
  2.  
    class="edit"
  3.  
    contenteditable="true"
  4.  
    @input="editInput"
  5.  
    placeholder="请输入消息内容"
  6.  
    v-html="innerHtml"
  7.  
    >
  8.  
    </div>

2、初始化方法

// 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。

  1.  
    initEditFun(){
  2.  
    editDiv = document.getElementsByClassName("edit")[0]
  3.  
     
  4.  
    editDiv.addEventListener("blur", () => {
  5.  
    // 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。
  6.  
    range = window.getSelection().getRangeAt(0)
  7.  
    })
  8.  
     
  9.  
    },

3、点击按钮时候,给可编辑区域中增加一个带有颜色的span

记得在你的功能样式中添加,可在app.vue,也可以在你项目其他公共区域

  1.  
    .selColor{
  2.  
    color: red;
  3.  
    }
点击事件:

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)    // 这一步就是添加当前区域对象到选区对象中

  1.  
    addStrBtnFun(){
  2.  
    const span = document.createElement("span")
  3.  
    span.innerText = this.addBtnText
  4.  
    span.className = 'selColor'
  5.  
    // 如果在页面刷新再点击编辑框之前就点击了按钮,此时range中并没有选区范围对象
  6.  
    if (range === "") {
  7.  
    let selection = window.getSelection()
  8.  
    selection.selectAllChildren(editDiv) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
  9.  
    /*
  10.  
    Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。
  11.  
    以上selectAllChildren方法,将div中子节点全部选中,collapseToEnd方法将选中区域取消,并且将光标定位到原区的末尾。
  12.  
    */
  13.  
    selection.collapseToEnd()
  14.  
    range = window.getSelection().getRangeAt(0) // 无论哪一步都需要保存当前编辑框的选区对象
  15.  
    }
  16.  
    let sel = window.getSelection()
  17.  
    range.insertNode(span) // insertNode方法,在range选区开头插入一个节点
  18.  
    /*
  19.  
    removeAllRanges方法:删除之前的所有选区。
  20.  
    这一步的意义:因为当我们点击其他区域时,选区对象已经改变,不再是编辑框中的选区对象,这时候我们接下来的操作都不会符合我们想象中的样子
  21.  
    */
  22.  
    sel.removeAllRanges()
  23.  
    sel.addRange(range) // 这一步就是添加当前区域对象到选区对象中,所以选区对象会再次指向编辑框中的选区,不会出现别的错误操作。
  24.  
    sel.collapseToEnd()
  25.  
    },

4、我们修改span内容,让他的样式消失方法

1)首先要用到  他的@input事件
@input="editInput"
  1.  
    editInput(e) {
  2.  
    console.log(e);
  3.  
    console.log(e.target.children);
  4.  
    },
这是它的默认拿到的数据 

学新通

如果插入span以后,可以看到e.target.children多了span信息

学新通

 拿我们页面的edit  div下面所有的span,如果哪个内容被改变,就去掉它的class
  1.  
    editInput(e) {
  2.  
    console.log(e);
  3.  
    console.log(e.target.children);
  4.  
    if(e.target.children.length>0){
  5.  
    this.editSpanClass()
  6.  
    }
  7.  
    },
  8.  
    editSpanClass(){
  9.  
    let spanAll = document.querySelectorAll('.edit span')
  10.  
    spanAll.forEach(item=>{
  11.  
    if(item.innerHTML != this.addBtnText){
  12.  
    item.className = ''
  13.  
    }
  14.  
    })
  15.  
    },

源码分享:tinymce.vue

  1.  
    <template>
  2.  
    <div id="editDivBody">
  3.  
    <el-button @click = 'addStrBtnFun'>{{addBtnText}}</el-button>
  4.  
    <el-button @click = 'getNowContent'>获取当前内容</el-button>
  5.  
    <div
  6.  
    class="edit"
  7.  
    contenteditable="true"
  8.  
    @input="editInput"
  9.  
    placeholder="请输入消息内容"
  10.  
    v-html="innerHtml"
  11.  
    >
  12.  
    </div>
  13.  
     
  14.  
    {{innerHtml}}
  15.  
    <hr>
  16.  
    {{innerText}}
  17.  
    </div>
  18.  
    </template>
  19.  
     
  20.  
    <script>
  21.  
    let editDiv = null //编辑元素
  22.  
    let range = "" //选区
  23.  
    export default {
  24.  
    data () {
  25.  
    return {
  26.  
    addBtnText:'插入员工昵称',
  27.  
    // 赋值
  28.  
    innerHtml:'haoxing<h1>111</h1>',
  29.  
    innerText:'',
  30.  
    // 获取当前最新数据
  31.  
    getDataTime:1000,
  32.  
    getDataTimeInter:null,
  33.  
    }
  34.  
    },
  35.  
    mounted() {
  36.  
    this.initEditFun()
  37.  
    },
  38.  
    methods:{
  39.  
    initEditFun(){
  40.  
    editDiv = document.getElementsByClassName("edit")[0]
  41.  
     
  42.  
    editDiv.addEventListener("blur", () => {
  43.  
    // 这一步是保留住edit编辑框中的选区的范围对象。否则失焦后,getSelection()方法返回的选区对象已经不再是编辑框了,已经获取不到编辑框中的范围对象了。
  44.  
    range = window.getSelection().getRangeAt(0)
  45.  
    })
  46.  
     
  47.  
    },
  48.  
    editInput(e) {
  49.  
    console.log(e);
  50.  
    console.log(e.target.children);
  51.  
    if(e.target.children.length>0){
  52.  
    this.editSpanClass()
  53.  
    }
  54.  
    },
  55.  
    editSpanClass(){
  56.  
    let spanAll = document.querySelectorAll('.edit span')
  57.  
    spanAll.forEach(item=>{
  58.  
    if(item.innerHTML != this.addBtnText){
  59.  
    item.className = ''
  60.  
    }
  61.  
    })
  62.  
    },
  63.  
    addStrBtnFun(){
  64.  
    const span = document.createElement("span")
  65.  
    span.innerText = this.addBtnText
  66.  
    span.className = 'selColor'
  67.  
    // 如果在页面刷新再点击编辑框之前就点击了按钮,此时range中并没有选区范围对象
  68.  
    if (range === "") {
  69.  
    let selection = window.getSelection()
  70.  
    selection.selectAllChildren(editDiv) // selectAllChildren把指定元素的所有子元素设为选中区域,并取消之前的选中区域。不包括node节点本身。
  71.  
    /*
  72.  
    Selection.collapseToEnd() 方法的作用是取消当前选区,并把光标定位在原选区的最末尾处,如果此时光标所处的位置是可编辑的,且它获得了焦点,则光标会在原地闪烁。
  73.  
    以上selectAllChildren方法,将div中子节点全部选中,collapseToEnd方法将选中区域取消,并且将光标定位到原区的末尾。
  74.  
    */
  75.  
    selection.collapseToEnd()
  76.  
    range = window.getSelection().getRangeAt(0) // 无论哪一步都需要保存当前编辑框的选区对象
  77.  
    }
  78.  
    let sel = window.getSelection()
  79.  
    range.insertNode(span) // insertNode方法,在range选区开头插入一个节点
  80.  
    /*
  81.  
    removeAllRanges方法:删除之前的所有选区。
  82.  
    这一步的意义:因为当我们点击其他区域时,选区对象已经改变,不再是编辑框中的选区对象,这时候我们接下来的操作都不会符合我们想象中的样子
  83.  
    */
  84.  
    sel.removeAllRanges()
  85.  
    sel.addRange(range) // 这一步就是添加当前区域对象到选区对象中,所以选区对象会再次指向编辑框中的选区,不会出现别的错误操作。
  86.  
    sel.collapseToEnd()
  87.  
    },
  88.  
    // 获取最新的内容
  89.  
    getNowContent(){
  90.  
    let editDiv = document.querySelectorAll('.edit')
  91.  
    this.innerHtml = editDiv[0].innerHTML
  92.  
    this.innerText = editDiv[0].innerText
  93.  
    }
  94.  
     
  95.  
    }
  96.  
    }
  97.  
    </script>
  98.  
    <style scoped>
  99.  
    .edit{
  100.  
    width: 400px;
  101.  
    height:200px;
  102.  
    border:1px solid #ccc;
  103.  
    }
  104.  
    .edit:focus {
  105.  
    outline: 0;
  106.  
    border-color: #409EFF;
  107.  
    }
  108.  
    </style>

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfjaahj
系列文章
更多 icon
同类精品
更多 icon
继续加载