首页  编辑  

判断可编辑内容中当前光标所在位置是否是开头还是末尾

Tags: /Node & JS/   Date Created:
在 contentEditable 的 HTML div/body元素中,如何判断当前光标所在的位置是开头(Ctrl+Home)还是在末尾(Ctrl + End)?
Contenteditable DIV - how can I determine if the cursor is at the start or end of the content

如下面的方法,无法对某些复杂DIV内部,有新行的时候,在新行前面得到的位置为0,和在页面顶部得到的pos = 0 一样:
const cursorPosition = () => {
  const sel = document.getSelection();
  sel.modify("extend", "backward", "paragraphboundary");
  const pos = sel.toString().length;
  if (sel.anchorNode != undefined) sel.collapseToEnd();
  return pos;
}
网上有一些解决方法,但是或多或少有一些问题,判断不准确,下面是一个比较完备的判断方法,供参考。
function isAtHome(el) {
    selection = window.getSelection();
    if (selection.focusOffset != 0) return false;
    range = selection.getRangeAt(0);
    tr = document.createRange();
    tr.setStart(el.firstChild, 0);
    tr.setEnd(selection.focusNode, selection.focusOffset);
    return tr.cloneContents().textContent.trim() == ""
}

function isAtEnd(el) {
    var selection = window.getSelection(),
    range = selection.getRangeAt(0);
    tr = document.createRange();
    tr.setStart(selection.focusNode, selection.focusOffset);
    tr.setEnd(el, el.childNodes.length);
    if (tr.cloneContents().textContent.trim() != "") return false;
    offset = selection.focusOffset;
    selection.modify("move", "forward", "character");
    if (offset == selection.focusOffset) {
        return true
    }
    selection.modify("move", "backward", "character");
    return false
}
使用方法示例,下面的代码,可以在文档开头按回车的时候,自动插入一个新的DIV,防止某些情况下,例如表格插在开头后,就无法插入新的DIV的问题:
function addNewDiv(el, first) {
    var newDiv = document.createElement('div');
    newDiv.innerHTML = '<br>';
    if (first) {
        el.insertBefore(newDiv, el.firstChild)
    } else {
        el.appendChild(newDiv)
    }
    var selection = window.getSelection();
    var range = selection.getRangeAt(0);
    range.setStart(newDiv.firstChild, 0);
    range.collapse(true);
    selection.removeAllRanges();
    selection.addRange(range);
    event.preventDefault()
}

document.body.addEventListener('keydown', function(event) {
	if (event.key === 'Enter' && !event.shiftKey) {
		if (isAtHome(this)) {
			addNewDiv(this, true)
		} else if (isAtEnd(this)) {
			addNewDiv(this, false)
		}
	}
}
此外下面的方法也是比较有效的方法:
<html>
<body>
<div contenteditable id="edit"><br/></div>
<script>

document.addEventListener("selectionchange", function() {
  updateCaretInfo(document.getElementById('edit'))
});

function updateCaretInfo(input) {

  function isAcceptableNode(node, side) {
    if (node === input) { return true }

    const childProperty = side === 'start' ? 'firstChild' : 'lastChild'
    while (node && node.parentNode && node.parentNode[childProperty] === node) {
      if (node.parentNode === input) {
        return true
      }

      node = node.parentNode
    }

    return false
  }

  function isAcceptableOffset(offset, node, side) {
    if (side === 'start') {
      return offset === 0
    }

    if (node.nodeType === Node.TEXT_NODE) {
      return offset >= node.textContent.replace(/\n$/, '').length
    }  else {
      return offset >= node.childNodes.length - 1
    }
  }

  function isAcceptableSelection(selection, side) {
    return selection &&
      selection.isCollapsed &&
      isAcceptableNode(selection.anchorNode, side) &&
      isAcceptableOffset(selection.anchorOffset, selection.anchorNode, side)
  }

  const selection = document.getSelection()
  const isAtStart = isAcceptableSelection(selection, 'start')
  const isAtEnd = isAcceptableSelection(selection, 'end')
  console.log("Start: ", isAtStart)
  console.log("End: ", isAtEnd)
}
</script>
</body>
</html>
参考资料: 
javascript - Contenteditable DIV - how can I determine if the cursor is at the start or end of the content - Stack Overflow
javascript - How to check if caret is at the end of contenteditable div? - Stack Overflow