web前端

可编辑div的光标定位问题

2021-12-17  本文已影响0人  姜治宇

上次分享了全局input增加敏感词检测的功能,有人私信我:如何解决光标漂移问题?
我们每次替换完文本,光标总是会飘到文本的最前面,而且不仅是input文本框,富文本编辑器(可编辑div)也有这个问题,如果直接用innerHTML方法直接替换内容的话,也会出现光标漂移的问题,如何解决呢?
我们先要搞清楚光标是个啥东西,其实光标这玩意本身就是一个js对象,其本身封装了一些属性和方法。
index.html:

    //================================过滤敏感词===================================================//
    document.addEventListener('input', debounce(handleWords, 800));
    function handleWords(e) {
      console.log('currentPos>>>', getCursorPos(e.target));
      let words = e.target.value;
      let wordType = '';
      if(e.target.nodeName === 'INPUT' && (!e.target.getAttribute('type') || e.target.getAttribute('type')=== 'text')) {
        console.log('text标签');
        wordType = 'input';
      }
      if (words && e.target.nodeName === 'TEXTAREA') {
        wordType = 'textarea';
      }
      if (!words && e.target.nodeName === 'DIV') {
        //可编辑文本框,包括富文本
        words = e.target.innerHTML;
        wordType = 'div';
      }
      //查询敏感词
      if (words && wordType) {
        //空白字符不做检测
        if(e.target.nodeName === 'DIV' && !e.target.innerText.replace(/\s+/g,'')){
          return false;
        }
        fetch(`/checkSensitiveWords`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
          body: `words=${words}&wordtype=${wordType}`
        }).then(response => response.text()).then(res => {
          if (res) {
            if (wordType === 'input') {
              e.target.value = res;
              setCursorPos(e.target, getCursorPos(e.target));//设置光标位置
            } else if (wordType === 'div') {

              pasteHtmlAtCaret(e.target, res);//不可使用e.target.innerHTML,会有光标漂移问题
            }

            e.target.dispatchEvent(new Event('input'));//更新ngModel视图
          }


        })
      }
    }
    function pasteHtmlAtCaret(el, html, selectPastedContent) {//参数1为要插入的html //参数2为boolean 是否选中插入的html 默认为false
      var sel, range;
      if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
          range = sel.getRangeAt(0);
          range.deleteContents();

          // Range.createContextualFragment() would be useful here but is
          // only relatively recently standardized and is not supported in
          // some browsers (IE9, for one)

          el.innerHTML = html;
          var frag = document.createDocumentFragment(), node, lastNode;
          while ((node = el.firstChild)) {
            lastNode = frag.appendChild(node);
          }
          var firstNode = frag.firstChild;
          range.insertNode(frag);

          // Preserve the selection
          if (lastNode) {
            range = range.cloneRange();
            range.setStartAfter(lastNode);
            if (selectPastedContent) {
              range.setStartBefore(firstNode);
            } else {
              range.collapse(true);
            }
            sel.removeAllRanges();
            sel.addRange(range);
          }
        }
      } else if ((sel = document.selection) && sel.type != "Control") {
        // IE < 9
        var originalRange = sel.createRange();
        originalRange.collapse(true);
        sel.createRange().pasteHTML(html);
        if (selectPastedContent) {
          range = sel.createRange();
          range.setEndPoint("StartToStart", originalRange);
          range.select();
        }
      }
    }
    //获取光标位置,仅对input有效
    function getCursorPos(input) {
      var pos = 0;
      // IE Support
      if (document.selection) {
        input.focus();
        var sel = document.selection.createRange();
        var selLen = document.selection.createRange().text.length;
        sel.moveStart('character', -input.value.length);
        pos = sel.text.length - selLen;
      }
      // Firefox support
      else if (input.selectionStart || input.selectionStart == '0')
        pos = input.selectionStart;

      return pos;
    }
    //设置光标位置,仅对input有效
    function setCursorPos(obj, pos) {

      if (obj.setSelectionRange) {//Firefox/Safari/Chrome/Opera
        obj.focus();
        obj.setSelectionRange(pos, pos);
      } else if (obj.createTextRange) { // IE
        var range = obj.createTextRange();
        range.collapse(true);
        range.moveEnd('character', pos);
        range.moveStart('character', pos);
        range.select();
      }
    }
    //防抖
    function debounce(fn, delay) {
      var timer = null;
      return function (...args) {
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          fn.apply(this, args)
        }, delay)
      }
    }

后台代码:

    @ApiOperation({ summary: "过滤敏感词", description: "过滤敏感关键词和特殊字符" })
    @Post('checkSensitiveWords')
    async checkSensitiveWords(@Body() params): Promise<any> {
        let words = params.words;
        let wordType = params.wordtype;
        let newWords = words;

        if (words) {
            let sensitiveWords = sensitiveWordsFunc();
            if (Array.isArray(sensitiveWords)) {
                sensitiveWords.forEach(item => {

                    let stripSpecWords = words;
                    if(wordType === 'input') {
                        stripSpecWords = words.replace(/\s+/g, "");//获取字符串信息,清除所有空格
                        stripSpecWords = stripSpecWords.replace(/[\-\_\,\!\|\~\`\(\)\#\$\%\^\&\*\{\}\:\;\"\L\<\>\?]/g, '');
                    }
                    let reg = new RegExp(item,"ig");
                    if (item && reg.test(stripSpecWords)) {
                        
                        newWords = stripSpecWords.replace(reg, '囗'.repeat(item.length));
                    }

                });
                console.log('newords>>>', newWords);
                return newWords;
            }

        }
        return newWords;
    }
上一篇 下一篇

猜你喜欢

热点阅读