Contenteditable display of last inserted html element

I use the contenteditable div as an input field to enter text and enter icons using the button (small html images) inside this text.

While the text is narrower than the contenteditable field, it is great.

As soon as the text is longer than the field (therefore it is partially hidden):

when I enter a text character, everything is fine, the last character is automatically displayed after pressing a key so that you can always see what you are typing. But when I enter the icon through the button, the icon is there, but it is hidden, because the contents of the field do not move to make the newly entered icon visible until I enter another text character.

Any solution for this, so that the last element entered (text or html) is always displayed to the user?

function pasteIcon(html) { var sel, range; if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { range = sel.getRangeAt(0); range.deleteContents(); var el = document.createElement("div"); el.innerHTML = html; var frag = document.createDocumentFragment(), node, lastNode; while ((node = el.firstChild)) { lastNode = frag.appendChild(node); } range.insertNode(frag); if (lastNode) { range = range.cloneRange(); range.setStartAfter(lastNode); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } } } else if (document.selection && document.selection.type != "Control") { document.selection.createRange().pasteIcon(html); } } $(document).ready(function() { $('.buttOn').click(function() { $('.contEd').focus(); pasteIcon('<img class="icOn" src="http://www.bmgstuff.com/files/interface/generator_frame/text_blood.png">'); }) }); 
 [contenteditable="true"] { display: inline; white-space: nowrap; overflow: hidden !important; text-overflow: inherit; -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; } [contenteditable="true"] br { display: none; } .contAiner { display: flex; } .buttOn { width: 24px; height: 24px; border: none; background: #666; color: white; } .contEd { height: 22px; text-align: center; width: 100px; line-height: 23px; color: black; font-size: 10.5px; font-family: arial; border: 1px solid black; } .icOn { width: 9px; height: 13px; top: 1px; position: relative; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="contAiner"> <input class="buttOn" type="button" value="B"> <div class="contEd" contenteditable="true" spellcheck="false" autocomplete="off"></div> </div> 

Here is jsFiddle

And so the source stream I took the function "pasteIcon".

PS: I tried to call key code 39 (right arrow) right after the pasteIcon function to simulate a keystroke, but that just didn't work.

+7
jquery html css contenteditable
source share
1 answer

You can simply scroll your editor to the inserted icon. Note the two lines of code immediately after the move. Hope it works as you expected :)

UPDATE:

To cover all cases, we need to check whether the inserted image is in the editor or outside it. First, add id to the editor element to find it simpler. Then we can use the getBoundingClientRect function, which returns the actual rectangle of the item on the screen. Finally, we compare the rectangles, and if the rectangle of the image is not inside the editor (imgRect.left <editorRect.left || imgRect.right> editorRect.right), then we scroll.

UPDATE 2:

While investigating the problem described in the last comments, I found that after a certain length of the edited content, the jQuery "offset" function returns inaccurate results. Most likely, this is because the leftOffset editor is not automatically updated in these circumstances. Finally, I changed the desired calculation of the scroll position to the image. The DOM element offsetLeft minus the editor element offsetLeft minus 1 (border size), and now it works great with any length of content.

 function pasteIcon(html) { var sel, range; if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { range = sel.getRangeAt(0); range.deleteContents(); var el = document.createElement("div"); el.innerHTML = html; var frag = document.createDocumentFragment(), node, lastNode; while ((node = el.firstChild)) { lastNode = frag.appendChild(node); } range.insertNode(frag); if (lastNode) { range = range.cloneRange(); range.setStartAfter(lastNode); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); var editorRect = $(contEdit)[0].getBoundingClientRect(); var imgRect = $(lastNode)[0].getBoundingClientRect(); if (imgRect.left < editorRect.left || imgRect.right > editorRect.right) { var actualLeft = $(lastNode)[0].offsetLeft - editorRect.left - 1; $(".contEd").scrollLeft(actualLeft); } } } } else if (document.selection && document.selection.type != "Control") { document.selection.createRange().pasteIcon(html); } } $(document).ready(function() { $('.buttOn').click(function() { $('.contEd').focus(); pasteIcon('<img class="icOn" src="http://www.bmgstuff.com/files/interface/generator_frame/text_blood.png">'); }) }); 
 [contenteditable="true"] { display: inline; white-space: nowrap; overflow: hidden !important; text-overflow: inherit; -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; } [contenteditable="true"] br { display: none; } .contAiner { display: flex; } .buttOn { width: 24px; height: 24px; border: none; background: #666; color: white; } .contEd { height: 22px; text-align: center; width: 100px; line-height: 23px; color: black; font-size: 10.5px; font-family: arial; border: 1px solid black; } .icOn { width: 9px; height: 13px; top: 1px; position: relative; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="contAiner"> <input class="buttOn" type="button" value="B"> <div id="contEdit" class="contEd" contenteditable="true" spellcheck="false" autocomplete="off"></div> </div> 
+5
source share

All Articles