JS文字操作库(亲测可用)

2023-12-13 04:53:13

使用

<template>
  <div id="examination" :style="{ '--themeColor': themeColors[0].color }">
             <div class="themeLi">
              <span class="main_right-btn">切换风格</span>
              <ul>
                <li
                  @click="onThemeColorChange(item.color)"
                  v-for="(item, index) in themeColors"
                  :key="index"
                  :style="{ 'background-color': item.color }"
                >
                  {{ item.name }}
                </li>
              </ul>
            </div>
  <li class="main_right-btn" @click="selectionColor('yellow')">题干标识</li>
  <li class="main_right-btn" @click="selectionColor('transparent')">取消标识</li>
  <li class="main_right-btn" @click="backText">撤销</li>
  <li class="main_right-btn" @click="goText">恢复</li>
  <li class="main_right-btn" @click="copyText">复制</li>
  <li class="main_right-btn" @click="cutstrText">剪切</li>
  <li class="main_right-btn" @click="pasteText">粘贴</li>
   <textarea id="examText" v-model="answerText" @input="onChangeAnswerText"></textarea>
        
  </div>
</template>

<script>
import Mark from '@/utils/TextMark'
export default {

  components: {

  },
  data() {
    return {
    // 当前的内容
      answerText: '',
      // 复制的文字
      copyVal: '',
      // 主题颜色
      themeColors: [
        { color: 'rgb(222, 235, 245)', name: '主题一' },
        { color: 'rgb(204, 225, 225)', name: '主题二' },
        { color: 'rgb(245, 232, 219)', name: '主题三' },
        { color: 'rgb(245, 219, 227)', name: '主题四' },
        { color: 'rgb(224, 223, 246)', name: '主题五' },
      ],
    }
  },
  mounted() {
    
  },
  computed: {
  },
  methods: {
   // 文本发生变化
    onChangeAnswerText(e) {
      Mark.stackedPrveTextStack(this.answerText)
    },
    // 撤销方法
    backText() {
      let text = Mark.revoke( this.answerText)
     if (typeof text == 'undefined') return
      this.answerText = text
    },
    //恢复方法
    goText() {
      let text = Mark.restore(this.answerText)
       if (typeof text == 'undefined') return
        this.answerText = text
    },
    // 剪切文字方法
    cutstrText() {
      this.copyVal = Mark.cutstr('examText').selectedText
      console.log('this.copyVal ', this.copyVal)
    },
    // 粘贴文字方法
    pasteText() {
      Mark.parsestr('examText', this.copyVal)
    },
    // 复制当前文字方法
    copyText() {
      this.copyVal = Mark.getSelectedText()?.trim()
    },
    // 标识文字方法
    selectionColor(color) {
      Mark.ColorizeSelection(color)
    },
  }
}
</script>
<style scoped  lang="less">
   .themeLi {
            display: flex;

            ul {
              display: none;
              li {
                font-size: 12px;
                line-height: 12px;
                margin-left: 6px;
                cursor: pointer;
                height: 12px;
                padding: 4px;
                margin-top: 4px;
                user-select: none;
              }
            }
            &:hover {
              ul {
                display: flex;
              }
            }
          }
</style>

库内容

const Mark = {
  // 标记方法 S
  GetNextLeaf: function (node) {
    while (!node.nextSibling) {
      node = node.parentNode
      if (!node) {
        return node
      }
    }
    var leaf = node.nextSibling
    while (leaf.firstChild) {
      leaf = leaf.firstChild
    }
    return leaf
  },
  GetPreviousLeaf: function (node) {
    while (!node.previousSibling) {
      node = node.parentNode
      if (!node) {
        return node
      }
    }
    var leaf = node.previousSibling
    while (leaf.lastChild) {
      leaf = leaf.lastChild
    }
    return leaf
  },
  // If the text content of an element contains white-spaces only, then does not need to colorize
  IsTextVisible: function (text) {
    for (var i = 0; i < text.length; i++) {
      if (text[i] != ' ' && text[i] != '\t' && text[i] != '\r' && text[i] != '\n') return true
    }
    return false
  },
  ColorizeLeaf: function (node, color) {
    if (!Mark.IsTextVisible(node.textContent)) return
    var parentNode = node.parentNode
    if (!parentNode) return
    // if the node does not have siblings and the parent is a span element, then modify its color
    if (!node.previousSibling && !node.nextSibling) {
      if (parentNode.tagName.toLowerCase() == 'span') {
        parentNode.style.backgroundColor = color
        return
      }
    }
    // Create a span element around the node
    var span = document.createElement('span')
    span.style.backgroundColor = color
    var nextSibling = node.nextSibling
    parentNode.removeChild(node)
    span.appendChild(node)
    parentNode.insertBefore(span, nextSibling)
  },
  ColorizeLeafFromTo: function (node, color, from, to) {
    var text = node.textContent
    if (!Mark.IsTextVisible(text)) return

    if (from < 0) from = 0
    if (to < 0) to = text.length

    if (from == 0 && to >= text.length) {
      // to avoid unnecessary span elements
      Mark.ColorizeLeaf(node, color)
      return
    }

    var part1 = text.substring(0, from)
    var part2 = text.substring(from, to)
    var part3 = text.substring(to, text.length)

    var parentNode = node.parentNode
    var nextSibling = node.nextSibling

    parentNode.removeChild(node)
    if (part1.length > 0) {
      var textNode = document.createTextNode(part1)
      parentNode.insertBefore(textNode, nextSibling)
    }
    if (part2.length > 0) {
      var span = document.createElement('span')
      span.style.backgroundColor = color
      var textNode = document.createTextNode(part2)
      span.appendChild(textNode)
      parentNode.insertBefore(span, nextSibling)
    }
    if (part3.length > 0) {
      var textNode = document.createTextNode(part3)
      parentNode.insertBefore(textNode, nextSibling)
    }
  },
  ColorizeNode: function (node, color) {
    var childNode = node.firstChild
    if (!childNode) {
      Mark.ColorizeLeaf(node, color)
      return
    }

    while (childNode) {
      // store the next sibling of the childNode, because colorizing modifies the DOM structure
      var nextSibling = childNode.nextSibling
      Mark.ColorizeNode(childNode, color)
      childNode = nextSibling
    }
  },
  ColorizeNodeFromTo: function (node, color, from, to) {
    var childNode = node.firstChild
    if (!childNode) {
      Mark.ColorizeLeafFromTo(node, color, from, to)
      return
    }

    for (var i = from; i < to; i++) {
      Mark.ColorizeNode(node.childNodes[i], color)
    }
  },
  ColorizeSelection: function (color) {
    if (!!window.ActiveXObject || 'ActiveXObject' in window) {
      document.execCommand('BackColor', false, color)
      return
    }
    // all browsers, except IE before version 9
    if (window.getSelection) {
      var selectionRange = window.getSelection()

      if (selectionRange.isCollapsed) {
        return
      }

      var range = selectionRange.getRangeAt(0)
      // store the start and end points of the current selection, because the selection will be removed
      var startContainer = range.startContainer
      var startOffset = range.startOffset
      var endContainer = range.endContainer
      var endOffset = range.endOffset
      // because of Opera, we need to remove the selection before modifying the DOM hierarchy
      selectionRange.removeAllRanges()

      if (startContainer == endContainer) {
        //同一个节点时,直接标记颜色
        Mark.ColorizeNodeFromTo(startContainer, color, startOffset, endOffset)
      } else {
        if (startContainer.firstChild) {
          var startLeaf = startContainer.childNodes[startOffset]
        } else {
          //标记第一段节点
          var startLeaf = Mark.GetNextLeaf(startContainer)
          Mark.ColorizeLeafFromTo(startContainer, color, startOffset, -1)
        }

        if (endContainer.firstChild) {
          if (endOffset > 0) {
            var endLeaf = endContainer.childNodes[endOffset - 1]
          } else {
            var endLeaf = Mark.GetPreviousLeaf(endContainer)
          }
        } else {
          var endLeaf = Mark.GetPreviousLeaf(endContainer)
          Mark.ColorizeLeafFromTo(endContainer, color, 0, endOffset)
        }

        while (startLeaf) {
          var nextLeaf = Mark.GetNextLeaf(startLeaf)
          Mark.ColorizeLeaf(startLeaf, color)
          if (startLeaf == endLeaf) {
            break
          }
          startLeaf = nextLeaf
        }
      }
    } else {
      // Internet Explorer before version 9
      document.execCommand('BackColor', false, color)
    }
  },
  // 标记方法 E
  // 粘贴方法
  parsestr(dom, myValue) {
    if (!myValue) return
    let myField = document.getElementById(dom)
    //IE support
    if (document.selection) {
      myField.focus()
      sel = document.selection.createRange()
      sel.text = myValue
      sel.select()
    }
    //MOZILLA/NETSCAPE support
    else if (myField.selectionStart || myField.selectionStart == '0') {
      var startPos = myField.selectionStart
      var endPos = myField.selectionEnd
      var restoreTop = myField.scrollTop
      myField.value =
        myField.value.substring(0, startPos) + myValue + myField.value.substring(endPos, myField.value.length)
      if (restoreTop > 0) {
        myField.scrollTop = restoreTop
      }
      myField.focus()
      myField.selectionStart = startPos + myValue.length
      myField.selectionEnd = startPos + myValue.length
    } else {
      myField.value += myValue
      myField.focus()
    }
     return  myField.value
  },
  // 复制方法
  getSelectedText() {
    return window.getSelection().toString()
  },
  // 剪切方法
  cutstr(id) {
    // 获取Textarea元素
    var textarea = document.getElementById(id)
    // 获取选中文本的起始和结束位置
    var selectionStart = textarea.selectionStart
    var selectionEnd = textarea.selectionEnd
    // 提取选中的文本
    var selectedText = textarea.value.substring(selectionStart, selectionEnd)
    // 删除选中的文本
    textarea.value = textarea.value.substring(0, selectionStart) + textarea.value.substring(selectionEnd)
    // 将选中的文本剪切到剪贴板
    document.addEventListener('cut', function (e) {
      // 将光标移到Textarea内部
      textarea.focus()
      e.clipboardData.setData('text/plain', selectedText)
      e.preventDefault()
    })

    // 执行剪切操作
    document.execCommand('cut')
        return {
      // 没删除的文本
      notValue:textarea.value,
      // 选中删除的文本
      selectedText:selectedText
    }
  },
  // 存储栈区
  textStack: [],
  // 栈区下标
  textStackIndex: 0,
  // 是否点击了恢复方法
  textStackRestore: false,
  // 撤销方法
  revoke: function (text) {
    if (typeof text == 'undefined') return
    if (Mark.textStackIndex <= 0) return
    Mark.textStackIndex--
    return Mark.textStack[Mark.textStackIndex]
  },
  //恢复方法
  restore: function (text) {
    if (typeof text == 'undefined') return
    if (Mark.textStackIndex >= Mark.textStack.length - 1) return
    Mark.textStackIndex++
    Mark.textStackRestore = true
    return Mark.textStack[Mark.textStackIndex]
  },
  // 清空栈方法(在切换保存区域的时候需要清空)
  clearStack() {
    Mark.textStack = []
    Mark.textStackIndex = 0
    Mark.textStackRestore = false
  },
  // 入栈方法
  stackedPrveTextStack(text) {
    if (typeof text == 'undefined') return
    Mark.textStack.push(text)
    if (Mark.textStackRestore) {
      Mark.textStack.splice(Mark.textStackIndex + 2)
    }
    Mark.textStackIndex = Mark.textStack.length - 1
  },
}
export default Mark

这块写的有问题,暂时没有解决方案

// 上一个栈
  prveTextStack: [],
  // 下一个栈
  nextTextStack: [],
  // 撤销方法
  revoke: function (text) {
    let content = Mark.prveTextStack.pop()
    if (text && content == text) {
      content = Mark.prveTextStack.pop()
    }
    if (typeof content != 'undefined') {
      Mark.nextTextStack.push(content)
    }
    return content
  },
  //恢复方法
  restore: function (text) {
    let content = Mark.nextTextStack.pop()
    if (text && content == text) {
      content = Mark.nextTextStack.pop()
    }
    if (typeof content != 'undefined') {
      Mark.prveTextStack.push(content)
    }
    return content
  },
  // 清空双栈方法(在切换保存区域的时候需要清空)
  clearStack() {
    Mark.nextTextStack = []
    Mark.prveTextStack = []
  },
  // 入栈方法
  stackedPrveTextStack(text) {
    if(typeof text == 'undefined')return;
      Mark.prveTextStack.push(text)
  },

文章来源:https://blog.csdn.net/qq_37547964/article/details/134583364
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。