javascript 在 contentEditable div 中插入元素之后立即设置插入符号位置
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/4834793/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Set caret position right after the inserted element in a contentEditable div
提问by Elie
I'm inserting an element into a contentEditable div but the browser sets the position of the cursor before the inserted element. Is it possible to set the cursor right after the inserted element so that the user keeps typing without having to re-adjust the cursor position?
我将一个元素插入到 contentEditable div 中,但浏览器在插入的元素之前设置了光标的位置。是否可以在插入元素之后立即设置光标,以便用户继续输入而无需重新调整光标位置?
回答by Tim Down
The following function will do it. DOM Level 2 Range objects make this easy in most browsers. In IE, you need to insert a marker element after the node you're inserting, move the selection to it and then remove it.
下面的函数将做到这一点。DOM Level 2 Range 对象在大多数浏览器中使这变得容易。在 IE 中,您需要在您插入的节点之后插入一个标记元素,将选择移动到它,然后将其删除。
Live example: http://jsfiddle.net/timdown/4N4ZD/
现场示例:http: //jsfiddle.net/timdown/4N4ZD/
Code:
代码:
function insertNodeAtCaret(node) {
    if (typeof window.getSelection != "undefined") {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var range = sel.getRangeAt(0);
            range.collapse(false);
            range.insertNode(node);
            range = range.cloneRange();
            range.selectNodeContents(node);
            range.collapse(false);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    } else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
        var html = (node.nodeType == 1) ? node.outerHTML : node.data;
        var id = "marker_" + ("" + Math.random()).slice(2);
        html += '<span id="' + id + '"></span>';
        var textRange = document.selection.createRange();
        textRange.collapse(false);
        textRange.pasteHTML(html);
        var markerSpan = document.getElementById(id);
        textRange.moveToElementText(markerSpan);
        textRange.select();
        markerSpan.parentNode.removeChild(markerSpan);
    }
}
Alternatively, you could use my Rangy library. The equivalent code there would be
或者,您可以使用我的Rangy 库。等效的代码将是
function insertNodeAtCaret(node) {
    var sel = rangy.getSelection();
    if (sel.rangeCount) {
        var range = sel.getRangeAt(0);
        range.collapse(false);
        range.insertNode(node);
        range.collapseAfter(node);
        sel.setSingleRange(range);
    }
}
回答by bob
If you're inserting an empty div, p or span, I believe there needs to be "something" inside the newly created element for the range to grab onto -- and in order to put the caret inside there.
如果您要插入一个空的 div、p 或 span,我相信在新创建的元素中需要有“东西”才能抓住范围 - 并且为了将插入符号放在那里。
Here's my hack that seems to work OK in Chrome. The idea is simply to put a temporary string inside the element, then remove it once the caret is in there.
这是我在 Chrome 中似乎可以正常工作的 hack。这个想法只是在元素中放置一个临时字符串,然后一旦插入符号在那里就将其删除。
// Get the selection and range
var idoc = document; // (In my case it's an iframe document)
var sel = idoc.getSelection();
var range = sel.getRangeAt(0);
// Create a node to insert
var p = idoc.createElement("p"); // Could be a div, span or whatever
// Add "something" to the node.
var temp = idoc.createTextNode("anything");
p.appendChild(temp);
// -- or --
//p.innerHTML = "anything";
// Do the magic (what rangy showed above)
range.collapse(false);
range.insertNode( p );
range = range.cloneRange();
range.selectNodeContents(p);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
// Clear the non
p.removeChild(p.firstChild);
// -- or --
//p.innerHTML = "";
回答by Dox
Here's what worked for me, using Rangy, in a VueJS context.
这是在 VueJS 上下文中使用 Rangy 对我有用的方法。
// When the user clicks the button to open the popup to enter
// the URL, run this function to save the location of the user's
// selection and the selected text.
newSaveSel: function() {
  if (this.savedSel) {
    rangy.removeMarkers(this.savedSel);
  }
  // Save the location of the selected text
  this.savedSel = rangy.saveSelection();
  // Save the selected text
  this.savedSelText = rangy.getSelection().toString();
  this.showLinkPopup = true;
  console.log('newSavedSel', this.savedSel);
},
surroundRange: function() {
  // Restore the user's selected text. This is necessary since
  // the selection is lost when the user stars entering text.
  if (this.savedSel) {
    rangy.restoreSelection(this.savedSel, true);
    this.savedSel = null;
  }
  // Surround the selected text with the anchor element
  var sel = rangy.getSelection();
  var range = sel.rangeCount ? sel.getRangeAt(0) : null;
  if (range) {
    // Create the new anchor element
    var el = document.createElement("a");
    el.style.backgroundColor = "pink";
    el.href = this.anchorHref;
    el.innerHTML = this.savedSelText;
    if (this.checked) {
      el.target = "_blank";
    }
    // Delete the originally selected text
    range.deleteContents();
    // Insert the anchor tag
    range.insertNode(el);
    // Ensure that the caret appears at the end
    sel.removeAllRanges();
    range = range.cloneRange();
    range.selectNode(el);
    range.collapse(false);
    sel.addRange(range);
    this.showLinkPopup = false; 
  }
},

