Loading

仿金山打字效果

背景

需要校验用户输入的内容和我们要求的内容保持一致,比如:签合同的时候需要用户根据我们的内容进行手抄

效果图

实现代码

<template>
  <div class="app">
    <h1>
      功能1:根据输入的内容去匹配正文,输入的内容显示在"输入内容"中,如果匹配正确的话则使用绿色标识,错误的的话使用红色标识
    </h1>
    <h3>《荷塘月色》</h3>
    <p>{{ original_content }}</p>
    <hr />
    输入内容:
    <p v-html="content"></p>
    <hr />
    <el-input
      :autosize="{ minRows: 2 }"
      type="textarea"
      :rows="2"
      placeholder="请输入内容"
      v-model="input_content"
      @input="handleInputContent"
    >
    </el-input>

    <h1>功能2:在原文中标识</h1>
    <h3>《荷塘月色》</h3>
    <p v-html="content_2" ref="p_ref"></p>
    <hr />
    <el-input
      :autosize="{ minRows: 2 }"
      type="textarea"
      :rows="2"
      placeholder="请输入内容"
      v-model="input_content_2"
      @input="handleInputContent_2"
    >
    </el-input>
    <hr />
    <h1>功能3:在输入中标识</h1>
    <h3>《荷塘月色》</h3>
    <p>{{ content_3 }}</p>
    <hr />
    输入的内容:
    <div
      class="text-box"
      contenteditable="true"
      @input="handleInputContent_3"
      ref="test_ref"
    ></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      original_content:
        '路上只我一个人,背着手踱着。这一片天地好像是我的;我也像超出了平常旳自己,到了另一世界里。我爱热闹,也爱冷静;爱群居,也爱独处。像今晚上,一个人在这苍茫旳月下,什么都可以想,什么都可以不想,便觉是个自由的人。白天里一定要做的事,一定要说的话,现在都可不理。这是独处的妙处,我且受用这无边的荷香月色好了。',

      content: '',
      input_content: '',

      original_content_2:
        '路上只我一个人,背着手踱着。这一片天地好像是我的;我也像超出了平常旳自己,到了另一世界里。我爱热闹,也爱冷静;爱群居,也爱独处。像今晚上,一个人在这苍茫旳月下,什么都可以想,什么都可以不想,便觉是个自由的人。白天里一定要做的事,一定要说的话,现在都可不理。这是独处的妙处,我且受用这无边的荷香月色好了。',
      content_2:
        '路上只我一个人,背着手踱着。这一片天地好像是我的;我也像超出了平常旳自己,到了另一世界里。我爱热闹,也爱冷静;爱群居,也爱独处。像今晚上,一个人在这苍茫旳月下,什么都可以想,什么都可以不想,便觉是个自由的人。白天里一定要做的事,一定要说的话,现在都可不理。这是独处的妙处,我且受用这无边的荷香月色好了。',
      input_content_2: '',

      input_content_3: '',
      content_3:
        '路上只我一个人,背着手踱着。这一片天地好像是我的;我也像超出了平常旳自己,到了另一世界里。我爱热闹,也爱冷静;爱群居,也爱独处。像今晚上,一个人在这苍茫旳月下,什么都可以想,什么都可以不想,便觉是个自由的人。白天里一定要做的事,一定要说的话,现在都可不理。这是独处的妙处,我且受用这无边的荷香月色好了。',
    }
  },
  directives: {
    convert: {
      update(el, binding) {
        console.log(el, binding)
        // el.innerHTML = '<span class="error">错误了</span>'
      },
    },
  },
  methods: {
    handleInputContent(value) {
      // 解决输入特殊字符后出现问题,要手动转义
      value = this.escapingSpecialCharacters(value)

      const reg = new RegExp('^' + value + '$')
      const match_result = reg.test(this.original_content)
      const str_length = value.length

      if (match_result) {
        this.content =
          "<span class='right'>" +
          this.original_content.substring(0, str_length) +
          '</span>'
        this.original_content.slice(str_length - 1)
      } else {
        // 记录此次需要匹配的原始内容
        let original_content_search_str = this.original_content.substring(
          0,
          str_length
        )
        let match_index = 0
        for (let i = 1; i <= value.length; i++) {
          const search_str = value.slice(0, i)
          if (original_content_search_str.includes(search_str)) {
            match_index = i
          } else {
            match_index = i - 1
            break
          }
        }

        this.content =
          "<span class='right'>" +
          value.substring(0, match_index) +
          "</span><span class='error'>" +
          value.substring(match_index) +
          '</span>'
      }
    },
    handleInputContent_2(value) {
      // 解决输入特殊字符后出现问题,要手动转义
      value = this.escapingSpecialCharacters(value)

      const reg = new RegExp('^' + value + '$')
      const match_result = reg.test(this.original_content_2)
      const str_length = value.length

      if (match_result) {
        this.content_2 =
          "<span class='right'>" +
          this.original_content_2.substring(0, str_length) +
          '</span>' +
          this.original_content_2.substring(str_length)
      } else {
        const right_el = this.$refs.p_ref.querySelector('.right')
        if (right_el) {
          const right_length = right_el.textContent.length
          this.content_2 =
            "<span class='right'>" +
            this.original_content_2.substring(0, right_length) +
            '</span>' +
            "<span class='error'>" +
            this.original_content_2.substring(right_length, str_length) +
            '</span>' +
            this.original_content_2.substring(str_length)
        } else {
          this.content_2 =
            "<span class='error'>" +
            this.original_content_2.substring(
              0,
              this.original_content_2.length
            ) +
            '</span>'
        }
      }
    },
    handleInputContent_3(e) {
      const input_content = e.target.textContent

      const reg_input_content = this.escapingSpecialCharacters(input_content)
      const reg = new RegExp('^' + reg_input_content + '$')
      const is_validate = reg.test(this.content_3)
      if (is_validate) {
        e.target.innerHTML = "<span class='right'>" + input_content + '</span>'
      } else {
        let right_index = -1
        let compare_str = ''
        for (let i = 0; i < input_content.length; i++) {
          const str = input_content[i]
          compare_str += str
          const new_compare_str = this.escapingSpecialCharacters(compare_str)
          const str_reg = new RegExp('^' + new_compare_str)
          if (str_reg.test(this.content_3)) {
            right_index = i
          } else {
            break
          }
        }

        if (right_index != -1) {
          e.target.innerHTML =
            "<span class='right'>" +
            input_content.substring(0, right_index + 1) +
            '</span>' +
            "<span class='error'>" +
            input_content.substring(right_index + 1) +
            '</span>'
        } else {
          e.target.innerHTML =
            "<span class='error'>" + input_content + '</span>'
        }
      }

      const selection = window.getSelection()
      if (selection.rangeCount > 0) {
        const range = selection.getRangeAt(0)
        // 插入新输入的文本节点
        range.deleteContents() // 清空选区内所有节点
        range.collapse(true)
        // 这里可能是因为 由于覆盖了 contenteditable属性元素中内容时新输入的内容会在最前面显示
        moveCursorToEnd(this.$refs.test_ref)
      }

      function moveCursorToEnd(el) {
        el.focus() // 设置焦点

        if (
          typeof window.getSelection != 'undefined' &&
          typeof document.createRange != 'undefined'
        ) {
          const range = document.createRange()
          range.selectNodeContents(el)
          range.collapse(false) // 将范围折叠到其结尾处
          const sel = window.getSelection()
          sel.removeAllRanges() // 清除所有选择范围
          sel.addRange(range) // 添加新的选择范围以将光标移至文本末尾
        }
      }
    },
    escapingSpecialCharacters(value) {
      return value
        .replaceAll('.', '\\.')
        .replaceAll('^', '\\^')
        .replaceAll('$', '\\$')
        .replaceAll('*', '\\\\*')
        .replaceAll('+', '\\+')
        .replaceAll('?', '\\\\?')
        .replaceAll('\\', '\\\\')
        .replaceAll('|', '\\|')
        .replaceAll('[', '\\[')
        .replaceAll(']', '\\]')
        .replaceAll('{', '\\{')
        .replaceAll('}', '\\}')
        .replaceAll(';', '\\\\;')
        .replaceAll('(', '\\(')
        .replaceAll(')', '\\)')
    },
  },
}
</script>

<style lang="less" scoped>
// /deep/.error {
//   color: #f00 !important;
// }
/deep/.right {
  color: #2e8b57;
}
/deep/.error {
  color: #f00;
}
.text-box {
  height: 30px;
  border: 1px solid orange;
}
</style>

具有"contenteditable"属性元素修改元素的内容,解决光标不在末尾的情况

<div class="edit_div" contenteditable="true"></div>
    <script>
      var edit_div_el = document.querySelector(".edit_div");
      edit_div_el.addEventListener("input", (e) => {
        e.preventDefault();
        const input_content = e.target.textContent;

        // 新增额外的内容
        e.target.innerHTML = input_content + "ok;";

        const selection = document.getSelection();
        console.log(selection);
        if (selection.rangeCount) {
          const range = document.createRange();
          range.selectNodeContents(edit_div_el);
          range.collapse(false);
          const sel = window.getSelection();
          sel.removeAllRanges(); // 清除所有选择范围
          sel.addRange(range);
        }
      });
    </script>
posted @ 2024-04-10 00:17  ^Mao^  阅读(11)  评论(0编辑  收藏  举报