Angular 7 move the caret in contenteditable div - javascript

I'm developing a basic html editor and I need to add a li to an ol when the return get pressed.
I've managed to do it but every time I update the innerhtml in my contenteditable div the caret reset to the start position. I'd like to position the caret inside the newly created li.
I've tried something like this but unfortunately it doesn't work:
setCursor() {
let el, el2, range, sel;
el = document.getElementById('editor');
range = document.createRange();
sel = window.getSelection();
if (el.childNodes.length > 0) {
el2 = el.childNodes[el.childNodes.length - 1];
range.setStartAfter(el2);
} else {
range.setStartAfter(el);
}
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
I've also tried replacing
range.setStartAfter(el2);
with
range.setStart(el2,0);
but still nothing happens.
Does anybody have any idea to make it working?
Thanks.

Related

Is there a way to not lose focus selection when replacing HTML with text in an contenteditable div?

I have a div tag that I make editable.
I do not want any HTML in that tag so I do not let users enter any. However, when the user does a copy / paste, it is not unlikely to include tags.
I have some jQuery code to capture the paste event and just in case I tried using my saveSelection() and restoreSelection() functions which I show below which work find in many situations but here they fail...
Fiddle: http://jsfiddle.net/9wm0oeah/2/
jQuery("#that_div").on("paste", function()
{
setTimeout(function()
{
// remove any HTML
var selection = saveSelection();
jQuery("#that_div").text(jQuery("#that_div").text());
restoreSelection(selection);
}, 0);
});
function saveSelection()
{
var sel;
if(document.selection)
{
return document.selection.createRange();
}
else
{
sel = window.getSelection();
if(sel.getRangeAt && sel.rangeCount > 0)
{
return sel.getRangeAt(0);
}
else
{
return null;
}
}
//NOTREACHED
}
function restoreSelection(range)
{
var sel;
if(document.selection)
{
range.select();
}
else
{
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange((range));
}
}
Do you have any idea why it fails?
When you change the DOM nodes inside your editable div, any range exists within or partially within that div has to change to accommodate the changes. If you completely replace the content, as your code does, the nodes that the range's boundaries were relative to are destroyed and the range has to revert to a default state.
You could use a character offset-based solution instead. For example: Can't restore selection after HTML modify, even if it's the same HTML

Set caret position at a specific position in contenteditable div

SEE BEFORE MARKING DUPLICATE/DOWNVOTING
The contenteditable div will not have child elements
I do not want to set the position at the end of the div
I do not want a cross-browser solution, only Chrome support required
Only vanilla JS, no libraries.
I have seen many many solutions. Many by Tim Down, and others. But none does work. I have seen window.getSelection, .addRange etc. but don't see how they apply here.
Here's a jsfiddle.
(Tried) Code:
var node = document.querySelector("div");
node.focus();
var caret = 10; // insert caret after the 10th character say
var range = document.createRange();
range.setStart(node, caret);
range.setEnd(node, caret);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
You need to position the caret within the text node inside your element, not the element itself. Assuming your HTML looks something like <div contenteditable="true">Some text</div>, using the firstChild property of the element will get the text node.
Updated jsFiddle:
http://jsfiddle.net/xgz6L/8/
Code:
var node = document.querySelector("div");
node.focus();
var textNode = node.firstChild;
var caret = 10; // insert caret after the 10th character say
var range = document.createRange();
range.setStart(textNode, caret);
range.setEnd(textNode, caret);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

Content-editable <span> tag unselects when the mouse button is released

I'm trying to use a content-editable span tag as a variable-width inline text input box. Everything is working fine, except I can't get it to select the whole text when I focus on the box. Tabbing to the editable span works fine, but when I click it, the mouse-up event seems to unselect the selection I just made. I'm using google chrome latest on Ubuntu.
http://jsfiddle.net/6b3tP/
Here's the HTML:
<div>
Inline editable <span contenteditable="true">spans</span> are hard to select.
</div>
And here's the JS:
$('span').focus(function() {
var text_node = this.firstChild;
var text = $(this).text();
var range = document.createRange();
range.setStart(text_node, 0);
range.setEnd(text_node, text.length);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
});
I'd suggest delaying doing the selecting using window.setTimeout() to allow the dust to settle before doing anything. You can also simplify the code a little.
Demo: http://jsfiddle.net/6b3tP/1/
Code:
$('span').focus(function() {
var span = this;
window.setTimeout(function() {
var range = document.createRange();
range.selectNodeContents(span);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}, 1);
});

contenteditable div issue when restore/saving selection

I have a div (#recommendTextArea) which is editable, in which that I try to modify the innerHTML of this div when a user clicks on a list (this is called .display_box), the function looks like this. Basically it appends a span to the innerHTML of the div and then it hides the friendList, upon hiding it also tries to restoreTheSelection and before appending the extra span I called saveSelection.
$(".display_box").live("click",function()
{
selRange = saveSelection();
console.log(selRange);
var username = $(this).attr('title');
var old = $("#recommendTextArea").html();
var content = old.replace(word, " "); //replacing #abc to (" ") space
var E ="<span contenteditable='false'>"+ username + "</span> ";
content = [content.slice(0, start), E, content.slice(start)].join('');
$("#recommendTextArea").html(content);
$("#friendsList").hide(function(){
restoreSelection(selRange);
});
});
I have the following function to restore and save selection:
function saveSelection() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restoreSelection(range) {
if (range) {
if (window.getSelection) {
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && range.select) {
range.select();
}
}
}
However this doesn't work as expected, the cursor is no where to be seen when I click on an item. What am I doing wrong here?
You have a few issues:
1) Timing: the "click" event is way too late to grab selection (ALWAYS debug this, it's super easy to see the editable DIV has long lost focus and selection by that time). Use "mousedown" instead.
2) You can't store selection range like this - changing the selection context (in your case the innerHTML of the commonAncestorContainer) will wipe that range (for some reason even cloned range objects get wiped for me). If you manage to get a copy (via jQuery.extend for example) it will become invalid because the text node inside is not guaranteed to remain the same. My best guess is to go with storing start/end offset and if needed the related nodes as required by the range. Restore the range properties after the HTML is modified.
3) As with 1) focus is crucial to maintain selection, so that click on the list.. make sure you prevent the default before exiting the handler so focus and you new selection will remain in the DIV.
Can't figure out the exact use case from your code but this is my test sample and you can adjust from here as needed: http://jsfiddle.net/damyanpetev/KWDf6/

Content Editable DIV Clicking/caret Setting

I have a standard content editable div:
<div id="profileBody" class="customInputDiv" contentEditable='true' autocomplete="off"></div>
Basically I want to detect whereabouts the cursor is on a certain event (preferably code that works onclick, keydown or on button).
Something like this:
onkeydown - save caret position within the div, then set caret position.
This sounds slightly silly, but is required because on certain events I will be changing the content slightly and when doing the cursor position changes which is very annoying.
I hate doing questions without examples of what I've tried but if you read previous questions I've always provide what I've tried very detailed, just hit a dead end here!
function setCaret() {
var el = document.getElementById("editable");
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[2], 5);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.focus();
}
​
Maybe a good way of parsing the data from window.getSelection();?
Found: http://jsfiddle.net/s5xAr/3/ But i dont want the -- just want it to get the caret position using a function and set it e.g.
getCaretPOS();
//do something
setCaretPOS();
This code will set it but getting it seems to be the issue..
To get the caret position, use this function:
function getCaretPosition(editableDiv) {
var caretPos = 0, containerEl = null, 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;
}
This should work fine for most browsers (leaving IE, of course). You seem to have IE covered with you setCaret function.
That function was from another SO answer. Have a look at the comments as well.
Demo

Categories

Resources