How to get the caret position of a contenteditable div that contains images

I have this contenteditable div

<div contenteditable="true" id="text">minubyv<img src="images/smiley/Emoji Smiley-01.png" class="emojiText" />iubyvt</div> 

Here is a description of the code output image enter image description here

so I want to get the position of the carriage div and suppose the cursor is after the last character. And this is my code to get the caret position

 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; } var update = function() { console.log(getCaretPosition(this)); }; $('#text').on("mousedown mouseup keydown keyup", update); 

But the problem is that it returns 6 instead of 14 . The carriage position returns to 0 after the image. Please there is a way that I can get the position of the carriage 14 in this case.

EDIT

I also want to insert an element starting at the caret position. so this is my function to do it

 selectStart = 0; var update = function() { selectStart = getCaretPosition(this); }; function insertEmoji(svg){ input = $('div#text').html(); beforeCursor = input.substring(0, selectStart); afterCursor = input.substring(selectStart, input.length); emoji = '<img src="images/smiley/'+svg+'.png" class="emojiText" />'; $('div#text').html(beforeCursor+emoji+afterCursor); } 
+6
source share
1 answer

See Tim Down's answer to Get the start and end offset of a range relative to its parent container .

Try using the function that it should get for the select index with nested elements, for example:

 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; } return caretOffset; } var update = function() { console.log(getCaretCharacterOffsetWithin(this)); }; $('#text').on("mousedown mouseup keydown keyup", update); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div contenteditable="true" id="text">minubyv<img src="https://themeforest.net/images/smileys/happy.png" class="emojiText" />iubyvt</div> 

I wrote my own feature based on Tim Down's, which works the way you want it. I changed treeWalker to a NodeFilter.ELEMENT_NODE insted filter from NodeFilter.SHOW_TEXT , and now the <img/> elements are also processed inside our loop. I start by storing range.startOffset and then recursively check all nodes of the selection tree. If he finds img node, he will add only 1 to the position; if the current node element is different from our range.startContainer , then it adds that the length of the node. The position is changed by another lastNodeLength variable, which is added to charCount in each loop. Finally, it adds everything that remains in lastNodeLength to the charCount when it exists in the loop, and we have the correct end position of the caret, including image elements.

Final working code (it returns 14 at the end, exactly as needed and you want to)

 function getCharacterOffsetWithin_final(range, node) { var treeWalker = document.createTreeWalker( node, NodeFilter.ELEMENT_NODE, function(node) { var nodeRange = document.createRange(); nodeRange.selectNodeContents(node); return nodeRange.compareBoundaryPoints(Range.END_TO_END, range) < 1 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; }, false ); var charCount = 0, lastNodeLength = 0; if (range.startContainer.nodeType == 3) { charCount += range.startOffset; } while (treeWalker.nextNode()) { charCount += lastNodeLength; lastNodeLength = 0; if(range.startContainer != treeWalker.currentNode) { if(treeWalker.currentNode instanceof Text) { lastNodeLength += treeWalker.currentNode.length; } else if(treeWalker.currentNode instanceof HTMLBRElement || treeWalker.currentNode instanceof HTMLImageElement /* || treeWalker.currentNode instanceof HTMLDivElement*/) { lastNodeLength++; } } } return charCount + lastNodeLength; } var update = function() { var el = document.getElementById("text"); var range = window.getSelection().getRangeAt(0); console.log("Caret pos: " + getCharacterOffsetWithin_final(range, el)) }; $('#text').on("mouseup keyup", update); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div contenteditable="true" id="text">minubyv<img contenteditable="true" src="https://themeforest.net/images/smileys/happy.png" class="emojiText" />iubyvt</div> 
+6
source

Source: https://habr.com/ru/post/1012568/


All Articles