【Vue】div标签实现输入框,利用contenteditable=“true“属性的标签实现

推荐个链接,可以更好的查阅自己遇到的问题(点击此处即可跳转)

使用 div 实现 input、textarea 输入框

<template>
  <div class="content">
    <div class="main editTextList" >
      <div
        class="main-content dzm-textarea editText"
        contenteditable="true"
        placeholder="请输入内容"
        id="content"
        @compositionstart="navCompositionstart"
        @compositionend="navCompositionend"
        @keyup="navKeyup"
        @input="navInput"
        @focus="navFocus"
        @propertychange="navPropertychange"
        @paste="optimizePasteEvent"
        ref="comment"
      ></div>
      <div class="main-num" id="num"><span id="total">0</span>/100</div>
    </div>
    <div class="three">
      <!-- 选择表情 -->
      <el-popover trigger="hover" placement="right" width="500" v-model="visible">
        <ElxImg @pic="navPic"></ElxImg>
        <span class="idp-custom-emoticon four cont" slot="reference" style="color: #3370ff">选择表情</span>
      </el-popover>
      <!-- 发布 -->
      <el-button type="primary" @click="postComments" class="fr">发表评论</el-button>
    </div>
  </div>
</template>

<script>
import ElxImg from "./Image.vue";
import Api from "@/api/XXApi";

export default {
  components: {
    ElxImg
  },
  props: {},
  watch: {},
  computed: {},
  data() {
    return {
      lock: true,
      fullContent: "",
      visible: false,
      imgArr:[],
    };
  },
  created() {
    const clearSelection = () => {
      if (document.selection) {
        document.selection.empty();
      } else {
        document.getSelection().removeAllRanges();
      }
    };
    // 禁用右键contextmenu、copy、select、drag 等相关事件
    Array.from(document.querySelectorAll("img")).forEach((img) => {
      [
        "contextmenu",
        "select",
        "selectstart",
        "copy",
        "beforecopy",
        "dragstart",
        "mouseup",
      ].forEach((type) => {
        img["on" + type] = () => {
          clearSelection();
          return false;
        };
      });
    });
    // 不允许全选,可以选择部分元素;离开页面记得释放事件
    window.onkeydown = function (e) {
      if (
        e.keyCode === 65 &&
        (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey)
      ) {
        e.preventDefault();
        clearSelection();
        return false;
      }
    };
  },
  methods: {
    navPic(val) {
      let content = document.getElementById('content'); //内容
      var imgNode = document.createElement('img');  //图片
      let numText = document.getElementById("num");  //总数样式
      let totalText = document.getElementById("total");  //总数
      let innerLength = 0;
      // 什么条件下可以添加表情包
      if(totalText.innerText < 100){
        imgNode.src = val;
        content.appendChild(imgNode);
        // 共输入几个表情包
        this.imgArr = content.innerHTML.match(//g);
        // 输入的表情包占多少字符串(一个表情占据21个字符串)
        if(this.imgArr && this.imgArr.length > 0){
          innerLength = 21 * this.imgArr.length;
          totalText.innerText = (content.innerHTML.length - innerLength) + (this.imgArr.length * 5);
        }else{
          totalText.innerText = content.innerHTML.length;
        }
      }else{
        content.style.borderColor = "red";
        numText.style.color = "red";
      }
      // this.keepCursorEnd()
      // this.keepCursorEnd(event.target);
    },
    // 字数限制
    addInput(event) {
      let numText = document.getElementById("num");
      let totalText = document.getElementById("total");
      let content = document.getElementById("content");
      let _words = content.innerText;
      if (this.lock) {
        let num = _words.length;
        if (num >= 100) {
          num = 100;
          if (
            event.target.style.borderColor == ("red" || "rgb(205, 205, 205)")
          ) {
            event.target.innerText = this.fullContent;
          } else {
            event.target.innerText = _words.substring(0, 100);
            content.style.borderColor = "red";
            numText.style.color = "red";
            this.fullContent = _words.substring(0, 100);
          }
          this.keepCursorEnd(event.target);
        } else {
          content.style.borderColor = "#CDCDCD";
          numText.style.color = "#CDCDCD";
          this.fullContent = "";
        }
        totalText.innerText = num;

        // 输入的内容中,表情包占多少字符串(一个表情占据21个字符串)
        if(content.innerHTML){
          let newText = (totalText.innerText) * 1;
          this.imgArr = content.innerHTML.match(//g);
          if(this.imgArr && this.imgArr.length) totalText.innerText = newText + (this.imgArr.length * 5);
        }
        
      } else if (this.fullContent) {
        // 目标对象:超过100字时候的中文输入法
        // 原由:虽然不会输入成功,但是输入过程中字母依然会显现在输入框内
        // 弊端:谷歌浏览器输入法的界面偶尔会闪现
        event.target.innerText = this.fullContent;
        this.lock = true;
        this.keepCursorEnd(content.innerHTML);
      }
    },
    // 发布
    postComments() {
      var content = document.getElementById('content');
      if(content.innerHTML){
        // 正则表达式匹配标签
        const regex = //g;
        // 替换标签为${11}的形式,实现每个表情占5个字符(这个可以根据需求来自定义)
        const replacedHtml = content.innerHTML.replace(regex, '${$1}');

        Api.commentSubmit({
            commentId: 0,
            content: replacedHtml,
            replyUid: 0,
            sourceId : this.contentId,
            sourceType: 'cms',
        })
        .then((res) => {
            this.$notifySuccess('评论成功');
            this.initData();
            if(this.$refs.comment) this.$refs.comment.innerText = '';
            totalText.innerText = 0;
        });
      }
    },
    // 如果需求要求超过字数后还可以中间输入内容,
    // 请忽略掉fullContent有关的地方,主要位置addInput()

    // 中文输入法问题(如果是禁止输入空格的需求,需用此方法,针对Firefox浏览器中五笔输入法,刚敲键盘,焦点会自动空一格,选择输入内容空格才会消失,直接oninput禁止输入空格会导致五笔输入法无法输入中文)
    navCompositionstart() {
      this.lock = false;
    },
    navCompositionend(event) {
      this.lock = true;
      this.addInput(event);
    },
    navKeyup(event) {
      this.addInput(event);
    },
    navInput(event) {
      this.addInput(event);
    },
    navPropertychange(event) {
      this.addInput(event);
    },
    navFocus() {
      let str = this.$refs.comment;
      this.keepCursorEnd(str);
    },
    // 监听粘贴div(contenteditable = "true")富文本转为纯文本对内容进行处理
    optimizePasteEvent(e) {
      e.stopPropagation();
      e.preventDefault();
      let text = "",
        event = e.originalEvent || e;
      if (event.clipboardData && event.clipboardData.getData) {
        text = event.clipboardData.getData("text/plain");
      } else if (window.clipboardData && window.clipboardData.getData) {
        text = window.clipboardData.getData("text");
      }

      if (document.queryCommandSupported("insertText")) {
        document.execCommand("insertText", false, text);
      } else {
        document.execCommand("paste", false, text);
      }
    },
    // 定位div(contenteditable = "true");超过字数光标定位到末端 将光标重新定位到内容最后
    keepCursorEnd(obj) {
      // ie11 10 9 firefox safari
      if (window.getSelection) {
        // 解决firefox不获取焦点无法定位问题
        obj.focus();
        // 创建range
        let range = window.getSelection();
        // range 选择obj下所有子内容
        range.selectAllChildren(obj);
        // 光标移至最后
        range.collapseToEnd();
      } else if (document.selection) {
        //ie10 9 8 7 6 5
        // 创建选择对象
        let range = document.selection.createRange();
        //range定位到obj
        range.moveToElementText(obj);
        //光标移至最后
        range.collapse(false);
        range.select();
      }
    },
  },
  mounted() {},
};
</script>

<style lang="scss" scoped>
  /* 输入框 */
.dzm-textarea {
  background: none;
  outline: none;
  padding: 10px 10px 30px;
  border: 1px solid #eeeeee;
  border-radius: 4px;
  word-wrap: break-word;
  word-break: break-all;
  -webkit-user-modify: read-write-plaintext-only;
}
/* 输入框为空时显示 placeholder */
.dzm-textarea:empty:before {
  content: attr(placeholder);
  color: #cdcdcd;
}
/* 输入框获取焦点时移除 placeholder */
.dzm-textarea:focus:before {
  // content: none;
  line-height:18px;
}
[contenteditable]:focus{
  outline: none;
  border: 1px solid #5d84e9; 
}
.main-num {
  position: absolute;
  bottom: 10px;
  right: 15px;
  color: #cdcdcd;
  line-height: 16px;
  text-align: right;
}
.content {
  .topTitle {
    margin-top: 20px;
    .two {
      color: #999999;
      font-size: 12px;
    }
  }
  .editTextList{
    position:relative;
    .editText {
      min-height: 100px;
      margin-top: 10px;
      background-color: #ffffff;
      padding-bottom:20px;
      &:after {
        color:#333333;
        line-height: 18px;
      }
      /deep/img{
        width: 22px; 
        height: 22px; 
        margin-right: 2px;
        vertical-align: middle;
        display: inline-block;
        position: relative;
        top:-2px;
        // 处理右键不可复制图片
        -webkit-user-drag: none;
				-khtml-user-drag: none;
				-moz-user-drag: none;
				-o-user-drag: none;
				user-drag: none;
				-webkit-user-select: none;
				-khtml-user-select: none;
				-moz-user-select: none;
				-o-user-select: none;
				user-select: none;
				pointer-events: none;
      }
    }
    .numTotal{
      position:absolute;
      bottom:10px;
      right:10px;
      color: #333333;
    }
  }
 
  .three {
    margin-top: 15px;
    overflow: hidden;

    .four {
      margin-top: 20px;
    }
  }
  .list {
    overflow: hidden;
    padding-bottom: 15px;
    .box {
      margin: 15px 0 10px 0;
      overflow: hidden;
      border-bottom: 1px solid #eeeeee;
      padding-bottom: 20px;
      .img {
        width: 50px;
        height: 50px;
        border-radius: 50%;
      }
      .userName{
            width:50px;
            height:50px;
            line-height:50px;
            text-align: center;
            color: #ffffff;
            font-size:14px;
            background: #5d84e9;
            border-radius: 50%;
        }
    }
  }
  .cont:hover {
    cursor: pointer;
  }
  .navBox {
    background: #f6f6f6;
    padding: 5px 10px;
    margin-top: 10px;
    overflow: hidden;
  }
}
.navText{
    margin-top:10px;
    overflow: hidden;
    line-height: 18px;
    color:#333333;
    word-wrap: break-word;  //换行
    /deep/img {
      width: 22px !important; 
      height: 22px !important; 
      // 右键不允许复制
      -webkit-user-drag: none;
      -khtml-user-drag: none;
      -moz-user-drag: none;
      -o-user-drag: none;
      user-drag: none;
      -webkit-user-select: none;
      -khtml-user-select: none;
      -moz-user-select: none;
      -o-user-select: none;
      user-select: none;
      pointer-events: none; 
    }
}
.huifu{
  width:99%;
  font-size:12px;
  overflow: hidden;
  float:right;
  margin-bottom:10px;
}
.hui{
  width:93%;
}
.foot{
  width:100%;
  color: #6980ff;
  font-size: 12px;
  padding-bottom: 20px;
  margin-top:20px;
  transform: all 3s;
  overflow: hidden;
}
.el-button--whiteBackground:focus,
.el-button--whiteBackground:hover {
  background: none;
  border-color: none;
  color: none;
}
.el-button--whiteBackground.is-active,
.el-button--whiteBackground:active {
  background: none;
  border-color: none;
  color: none;
}
.textDialog{
  color:#FF3333;
  margin-top:5px;
  font-size: 13px;
}
img {
    -webkit-user-drag: none;
    -khtml-user-drag: none;
    -moz-user-drag: none;
    -o-user-drag: none;
    user-drag: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -o-user-select: none;
    user-select: none;
    pointer-events: none;
  }
</style>

以下代码块为表情包(多个图片组成的图片库)

<template>
  <div class="content">
    <div class="content">
       <img :src="item" alt="" v-for="(item,index) in imgSrc" :key="index" @click="selImg(item)">
    </div>
  </div>
</template>

<script>
export default {
    mixins:[],
    components:{},
    props:{},
    watch: {},
    computed: {
        imgSrc(){
            let arr = [];
            for (let i = 10; i <= 86; i++) {
                arr.push(__static__ + `/e/${i}.gif`);
            }
            return arr
        },
    },
    data() {
        return {
            cont:'',
        }
    },
    created() {},
    mounted() {},
    methods: {
        selImg(val){
            this.cont = val;
            this.$emit('pic', this.cont);
        }
    },
}
</script>
<style lang='scss' scoped>
</style>

【Vue】div标签实现输入框,利用contenteditable=“true“属性的标签实现_第1张图片

你可能感兴趣的:(vue,JS,javascript,前端,开发语言,vue)