编辑器基础contenteditable

<template>
  <div class="edit-container">
    <div id="myEditor" ref="myEditor" contenteditable="true" @blur="getBlur()" @keyup="statisticsNum()" />
    <div class="flex-between-center bottom-btn">
      <div class="flex-start-center">
        <div class="button" @click="openEmoji">插入表情</div>
        <div class="button" @click="addLink">插入超链接</div>
        <div class="button" @click="addMiniprogramLink">插入小程序</div>
      </div>
      <div class="statistics">已输入{{ total }}字</div>
    </div>
    <div v-if="isOpenEmoji" class="emoji-content">
      <div v-for="(item, index) in emojiList" :key="index" class="emoji-item" @click="addEmoji(index)">{{ item }}</div>
    </div>
  </div>
</template>
<script>
import emojiData from './emoji.json';
let sel;
let range;
export default {
  data() {
    return {
      total: 0,
      emojiList: [],
      isOpenEmoji: false
    };
  },
  methods: {
    // 字数统计
    statisticsNum() {
      const limitText = this.$refs.myEditor.innerHTML;
      const limitLen = limitText.length;
      if (limitText === '') {
        this.$refs.myEditor.blur();
      } else {
        // this.setFocus($('#myEditor'));
      }
      this.total = limitLen;
    },
    // 获取全部表情
    getEmoji() {
      for (const i in emojiData) {
        this.emojiList.push(emojiData[i].char);
      }
    },
    openEmoji() {
      this.isOpenEmoji = !this.isOpenEmoji;
    },
    // 添加表情
    addEmoji(index) {
      for (const i in this.emojiList) {
        if (index === Number(i)) {
          const face = this.emojiList[index] + '&nbsp;';
          this.insertHtml(face);
        }
      }
    },
    // 插入超链接
    addLink() {
      const link = `<a target='_blank' contenteditable='false' href='https://www.baidu.com/'>超链接(百度)</a>`;
      this.insertHtml(link);
    },
    // 插入小程序
    addMiniprogramLink() {
      const link = `<a target='_blank' contenteditable='false' href='https://www.baidu.com/' data-miniprogram-appid='111' data-miniprogram-path='https://www.baidu.com/'>虚拟小程序</a>`;
      this.insertHtml(link);
    },
    // 作用:失去焦点时获取光标的位置
    getBlur() {
      sel = window.getSelection();
      if (sel && sel.getRangeAt && sel.rangeCount) {
        range = sel.getRangeAt(0);
        // range.deleteContents();
      }
    },
    // 作用:在contenteditable="true"文本框光标处插入内容
    insertHtml(html) {
      // IE9 and non-IE
      if (window.getSelection) {
        if (sel && sel.getRangeAt && sel.rangeCount) {
          const el = document.createElement('div');
          el.innerHTML = html;
          const frag = document.createDocumentFragment();
          let node, lastNode;
          while ((node = el.firstChild)) {
            lastNode = frag.appendChild(node);
          }
          range.insertNode(frag);
          if (lastNode) {
            range = range.cloneRange();
            range.setStartAfter(lastNode);
            range.collapse(true);
            sel.removeAllRanges();
            sel.addRange(range);
          }
        }
      } else if (document.selection && document.selection.type !== 'Control') {
        // IE < 9
        document.selection.createRange().pasteHTML(html);
      }
      this.statisticsNum();
    },
    // 作用:设置光标始终在文本最后
    // 情景:利用contenteditable=”true”模拟输入框时,focus()方法会将光标定位在文本的首位,需要将光标挪到最后一位
    // 注意:根据实际情况判断是否需要这段代码
    setFocus(el) {
      el = el[0]; // jquery 对象转dom对象
      el.focus();
      range = document.createRange();
      range.selectNodeContents(el);
      range.collapse(false);
      sel = window.getSelection();
      // 判断光标位置,如不需要可删除
      if (sel.anchorOffset !== 0) {
        return;
      }
      sel.removeAllRanges();
      sel.addRange(range);
    }
  },
  created() {
    this.getEmoji();
  }
};
</script>
<style scoped>
.flex-between-center {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.flex-start-center {
  display: flex;
  justify-content: start;
  align-items: center;
}
.edit-container {
  position: relative;
  width: 500px;
  height: 300px;
  background: #859ffc;
  border: 1px solid #EBEEF5;
  margin: 20px;
  font-size: 12px;
}
#myEditor {
  box-sizing: border-box;
  width: 460px;
  height: 230px;
  background: #fff;
  margin: 20px auto 0;
  padding: 10px;
  line-height: 18px;
}
.bottom-btn {
  box-sizing: border-box;
  width: 460px;
  height: 30px;
  background: #fff;
  margin: 0 auto;
  padding: 0 10px;
  border-top: 1px solid #EBEEF5;
}
.button {
  margin-right: 10px;
  color: #57e;
  cursor: pointer;
}
.statistics {
  color: #999;
}
.emoji-content {
  width: 220px;
  height: 280px;
  background: #fff;
  border: 1px solid #EBEEF5;
  border-radius: 4px;
  position: absolute;
  top: 0px;
  left: 502px;
  display: flex;
  flex-wrap: wrap;
  padding: 10px;
  overflow: auto;
}
.emoji-item {
  width: 14%;
  font-size: 18px;
  list-style: none;
  text-align: center;
  cursor: pointer;
}
</style>

 

posted @ 2021-12-01 16:56  远看山有色  阅读(236)  评论(0)    收藏  举报