可编辑非input,texrarea+vue实现双向绑定(以div标签为例)

想法来源

关于输入,input标签和textarea标签可以满足我们90%以上的需求,但有些时候有些一些特殊需求,或者在改别人的项目时,换标签不方便,这个时候需求就没办法无法满足。
此时,我们就可以使用div等一些标签来实现

本文涉及知识点

属性:contenteditable是一个枚举属性,表示元素是否可被用户编辑。 如果可以,浏览器会修改元素的部件以允许编辑。
当标签加上这个属性时,就可进行内容编辑,来实现输入的效果。并且实现placeholder效果,如下

//html
<div>请在下方的框中输入你喜欢的句子</div>
    <div contenteditable placeholder='请输入文字' class="input"></div>
//css
  .input {
    
    
            width: 500px;
            height: 24px;
            line-height: 24px;
            font-size: 14px;
            padding: 5px 8px;
            border: 1px solid #ddd;
        }  
        .input:empty::before {
    
    
            content: attr(placeholder);
        }
       

如下
在这里插入图片描述

这里由于下面会用到,这里先直接列出
大部分知道contenteditable属性有 true 和 false。,但是不止这些

contenteditable属性值

contenteditable="" //表示元素是可编辑的
contenteditable="events"
contenteditable="caret"
//让div只能键入文本值
//兼容性不好,除了谷歌,火狐ie都不支持
contenteditable="plaintext-only"
contenteditable="true"//表示元素是可编辑的
contenteditable="false" //表示元素是不可编辑的

至于contenteditable=“events”,contenteditable="caret"我也没有查到是什么意思,但是下面用不上我们这里就不深挖了

双向绑定

我们都知道,div是无法使用 v-model,那么该如何实现双向绑定呢?

//html
 <div id="id">
        <div>请在下方的框中输入你喜欢的句子</div>
        <div contenteditable placeholder='请输入文字' class="input" v-html="content" @input="content=$event.target.innerHTML"></div>
        <div>{
    
    {
    
    content}}</div>
    </div>
<div contenteditable placeholder='请输入文字' v-html="item.content"></div>
//js
var id = new Vue({
    
    
            el: "#id",
            data: {
    
    
                content: "",
            },
        })

但是有时只想复制文本,却发现复制过来的是标签+标签样式,那么改如何处理?
这个时候就要用到contenteditable="plaintext-only"
但是实际使用上有很多问题,比如光标的问题,我们可以依靠组件来对这些问题进行处理

<div id="app">
    <m-contenteditable :child="content"></m-contenteditable>
    <m-contenteditable :child="content"></m-contenteditable>
    <div><pre v-html="content.txt"></pre></div>
</div>
<script>
//定义一个全局组件
Vue.component("mContenteditable",{
    
    
    props:{
    
    
        child:{
    
    
            type:Object,
            default:{
    
    
                txt:""
            }
        }
    },
    //数据
    data:function(){
    
    
        return {
    
    
            innerText:this.child.txt,
            lock:false
        }
    },
    //侦听器
    watch:{
    
    
        child:{
    
    
            handler(newValue, oldValue) {
    
    
                if(!this.lock) {
    
    
                    this.innerText=this.child.txt;
                };
           },
           deep:true
        }
    },
    //改变的方法
    methods:{
    
    
        changeTxt:function(e){
    
    
            this.child.txt=this.$el.innerHTML;
        }
    },
    //模板
    template:`<div class="box" contenteditable="true" v-html="innerText" @input="changeTxt" @focus="lock=true" @blur="lock=false"></div>`
});
new Vue({
    
    
    el:"#app",
    data:{
    
    
        content:{
    
    
            txt:"内容<img src=\"http://pub.idqqimg.com/lib/qqface/0.gif\" width=\"38\" height=\"38\">"
        }
    }
});

猜你喜欢

转载自blog.csdn.net/Yannnnnm/article/details/112250985