【问题标题】:Get word starting with special characters at caret in contenteditable div在 contenteditable div 的插入符号处获取以特殊字符开头的单词
【发布时间】:2017-05-07 17:14:44
【问题描述】:

我有一个contenteditable div,我需要知道当前插入符号位置的单词。我试过this solution,但问题是,它不能识别像@ 和~ 这样的特殊字符。所以如果一个词以~ 开头,比如~fool,我得到的是fool,而我期望的是~fool。所以我尝试修改解决方案,考虑到如果将选择移回后,遇到的字符不是空格,我会继续向后移动,直到遇到空格。这将是选择的开始。同样,我会继续前进,直到找到一个空间,这将标志着选择的结束。然后选择会给我这个词。为了获得插入符号的位置,我使用了this solution。结合起来,我的代码现在看起来像这样:

function getCaretPosition(editableDiv) {
  var caretPos = 0,
    sel, range;
  if (window.getSelection) {
    sel = window.getSelection();
    if (sel.rangeCount) {
      range = sel.getRangeAt(0);
      if (range.commonAncestorContainer.parentNode == editableDiv) {
        caretPos = range.endOffset;
      }
    }
  } else if (document.selection && document.selection.createRange) {
    range = document.selection.createRange();
    if (range.parentElement() == editableDiv) {
      var tempEl = document.createElement("span");
      editableDiv.insertBefore(tempEl, editableDiv.firstChild);
      var tempRange = range.duplicate();
      tempRange.moveToElementText(tempEl);
      tempRange.setEndPoint("EndToEnd", range);
      caretPos = tempRange.text.length;
    }
  }
  return caretPos;
}

function getCurrentWord() {
    var sel, word = "";
    if (window.getSelection && (sel = window.getSelection()).modify) {
        var selectedRange = sel.getRangeAt(0);
        sel.collapseToStart();
        sel.modify("move", "backward", "word");

        while (sel.toString() != " " && getCaretPosition($("#editor").get(0)) != 0) {
            sel.modify("move", "backward", "character");
            (sel = window.getSelection()).modify;
        }
        sel.modify("move", "forward", "character");      
        sel.modify("extend", "forward", "word");
        word = sel.toString();

        // Restore selection
        sel.removeAllRanges();
        sel.addRange(selectedRange);
    } else if ((sel = document.selection) && sel.type != "Control") {
        var range = sel.createRange();
        range.collapse(true);
        range.expand("word");
        word = range.text;
    }
    return word;
}

$(function () {

    $(document).on('keyup keydown paste cut mouseup',"#editor", function () {
        var word = getCurrentWord();
        console.log(word);
    });
});

但是这根本不起作用。那是没有问题。 1.问题2是,即使图片中有图片并且用户点击图片,处理程序也会继续返回图片之前的最后一个单词,而我期待的是一个空白字符串。谁能帮我解决这两个问题?

【问题讨论】:

  • 我讨厌成为那个人,但听起来在 2 个答案之间,您拥有为您的需求组合一个可行的解决方案所需的一切。您不必将任何一个答案都标记为正确,只需认为您有足够的解决方案来让自己工作:P
  • 我自己也在尝试一些东西,我不会懒惰地坐着等待别人的工作解决方案。
  • 抱歉,我无意暗示你很懒惰。只是意味着在两个答案之间,我认为一个答案潜伏着。
  • 不不,没关系,我只是说我也在尝试很多事情,没有太多运气..:)

标签: javascript jquery contenteditable


【解决方案1】:

我修改了getCurrentWord() 函数以使用基本的字符串方法从插入符号位置获取单词。该函数接受元素和位置并返回该位置的单词。

以下是更新后的功能。

function getCurrentWord(el, position) {
    // Get content of div
    var content = el.textContent;

    // Check if clicked at the end of word
    position = content[position] === ' ' ? position - 1 : position;

    // Get the start and end index
    var startPosition = content.lastIndexOf(' ', position);
    var endPosition = content.indexOf(' ', position);

    // Special cases
    startPosition = startPosition === content.length ? 0 : startPosition;
    endPosition = endPosition === -1 ? content.length : endPosition;

    return content.substring(startPosition + 1, endPosition);
}

函数首先获取元素的内容。接下来,它会检查用户是否点击了单词的末尾,如果是,则从该位置减去一个,以便indexOf 和lastIndexOf 在空格上正常工作。

对于开始位置和结束位置,有两种特殊情况需要处理。首先,单击最后一个元素。为此,startPosition 将是 -1,因为最后一个单词后面不能有空格。

其次,当点击第一个单词时,endPosition 将变为-1,因为第一个单词之前不能有空格。

即使第一个字符之前有空格,最后一个字符之后有空格,这两个条件也会起作用。

indexOf 和lastIndexOf 用于查找单词前后的空格,使用这些索引substring 将在该位置给出单词。

这里是 live demo 进行测试。

$(function() {
  function getCaretPosition(editableDiv) {
    var caretPos = 0,
      sel, range;
    if (window.getSelection) {
      sel = window.getSelection();
      if (sel.rangeCount) {
        range = sel.getRangeAt(0);
        if (range.commonAncestorContainer.parentNode == editableDiv) {
          caretPos = range.endOffset;
        }
      }
    } else if (document.selection && document.selection.createRange) {
      range = document.selection.createRange();
      if (range.parentElement() == editableDiv) {
        var tempEl = document.createElement("span");
        editableDiv.insertBefore(tempEl, editableDiv.firstChild);
        var tempRange = range.duplicate();
        tempRange.moveToElementText(tempEl);
        tempRange.setEndPoint("EndToEnd", range);
        caretPos = tempRange.text.length;
      }
    }
    return caretPos;
  }

  function getCurrentWord(el, position) {
    var word = '';

    // Get content of div
    var content = el.textContent;

    // Check if clicked at the end of word
    position = content[position] === ' ' ? position - 1 : position;

    // Get the start and end index
    var startPosition = content.lastIndexOf(' ', position);
    startPosition = startPosition === content.length ? 0 : startPosition;
    var endPosition = content.indexOf(' ', position);
    endPosition = endPosition === -1 ? content.length : endPosition;

    return content.substring(startPosition + 1, endPosition);
  }


  $('#editor').on('keyup keydown paste cut mouseup', function() {
    var caretPosition = getCaretPosition(this);
    var word = getCurrentWord(this, caretPosition);
    console.log(word);
  });
});
div {
  font-size: 18px;
  line-height: 1.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="editor" contenteditable>



  Lorem !p$um dolor $!t @met, con$ectetur @d!p!$!c!ng el!t, $ed do e!u$mod tempor !nc!d!dunt ut l@bore et dolore m@gn@ @l!qu@. Ut en!m @d m!n!m ven!@m, qu!$ no$trud exerc!t@t!on ull@mco l@bor!$ n!$! ut @l!qu!p ex e@ commodo con$equ@t. Du!$ @ute !rure dolor
  !n reprehender!t !n volupt@te vel!t e$$e c!llum dolore eu fug!@t null@ p@r!@tur.
</div>

【讨论】:

  • 仍有大量错误。例如,在您在小提琴中提供的示例内容中,如果您单击第三个单词 - dolor,并执行CTRL-B 使其变为粗体,然后再次单击它,它开始给出错误的单词。如果您双击一个单词来选择它,而不是记录该单词,而是记录错误的单词。
【解决方案2】:

这是一个非常简单的例子。有一个带有示例文本的 div:

<div contenteditable onclick="getCaretCharacterOffsetWithin(this)">some ~test content</div>

这是脚本。当我们点击 div 时,无论光标落在哪里,我们都会得到当前位置并吐出我们所在的单词。这个单词可以包含特殊字符,并且仅用空格分隔。

function getCaretCharacterOffsetWithin(element) {
    var caretOffset = 0;
    var doc = element.ownerDocument || element.document;
    var win = doc.defaultView || doc.parentWindow;
    var sel;
    if (typeof win.getSelection != "undefined") {
        sel = win.getSelection();
        if (sel.rangeCount > 0) {
            var range = win.getSelection().getRangeAt(0);
            var preCaretRange = range.cloneRange();
            preCaretRange.selectNodeContents(element);
            preCaretRange.setEnd(range.endContainer, range.endOffset);
            caretOffset = preCaretRange.toString().length;
        }
    } else if ( (sel = doc.selection) && sel.type != "Control") {
        var textRange = sel.createRange();
        var preCaretTextRange = doc.body.createTextRange();
        preCaretTextRange.moveToElementText(element);
        preCaretTextRange.setEndPoint("EndToEnd", textRange);
        caretOffset = preCaretTextRange.text.length;
    }
  console.log('caretOffset', caretOffset);
  word = getWordAtPosition(caretOffset, element);
  console.log('word', word);
    return caretOffset;
}

function getWordAtPosition(position, element) {
  var total_text = element.innerHTML;
  var current_word = "";
  var i = 0;
  var word_found = false;
  while(i < total_text.length) {
    if(total_text[i] != ' ')
      current_word += total_text[i];
    else if(word_found)
      return current_word;
    else
      current_word = "";
    if(i == position)
      word_found = true;
    i++;
  }
  return current_word;
}

这是一个工作示例:

https://codepen.io/anon/pen/dWdyLV

【讨论】:

  • 嗯。几乎可以工作。除了,在您提供的代码中,如果我单击单词的末尾,即现在光标在单词 ~test 的末尾闪烁,则显示的单词是 content,它是下一个由 a 分隔的单词空间。其次,如果我单击一个单词的开头,使光标在单词的第一个字符之前闪烁,对于单词~test,它返回~test,对于其他单词,如hello,不是以~,它返回&amp;nbsp;
  • 更不用说,如果你把~test这个词加粗,返回的词是&lt;b&gt;~test&lt;/b&gt;而不是~test,然后点击后面的content这个词仍然会产生&lt;b&gt;~test&lt;/b&gt;。
  • 首先想到的是:选择&lt;b&gt;~test&lt;/b&gt; 不合适吗?我认为最简单的做法是写出您想要指示单词分离的特殊字符的特定列表,然后从那里开始。
  • 这是您看到的众多问题之一。只需插入一个新行并单击换行符两侧的单词,看看会发生什么。
猜你喜欢
  • 2014-09-13
  • 2011-10-05
  • 2019-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-28
  • 1970-01-01
相关资源
最近更新 更多