Contenteditable set carret on a child node - javascript

I'm having some difficulties while trying to set the carret after an <i> tag inside a contenteditable.
Here is what I have :
<p contenteditable="true"><i>H</i><i>e</i><i>l</i><i>l</i><i>o</i></p>
How do I put the carret after the .. let's say 3rd <i> tag here?
I already tried this solution :
var el = document.getElementsByTagName('p')[0];
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[0], 3);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.focus();
But I don't know how to make it work with the position of the <i> tags instead of the chars.

var el = document.getElementsByTagName('p')[0];
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[3], 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
el.focus();
<p contenteditable="true"><i>H</i><i>e</i><i>l</i><i>l</i><i>o</i></p>
In p 5 child nodes are there, if you want to set caret at child node use range.setStart(el.childNodes[3], 0);

Related

Is there a way to select text through multiple elements?

var range = document.createRange();
var root_node = document.getElementById("test");
// Start at the `hello` element.
range.setStart(root_node.childNodes[0], 2);
// End in the `world` node
range.setEnd(root_node.childNodes[1], 2);
range.selectNodeContents(root_node);
let sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
<div id="test">
hello
<span>world</span>
</div>
I'd like to select text but because it's in different elements it hasn't been working. Is there a way to do this?
I don't mean to highlight both words in their entirety but portions of each word.
#Matt answer is in the right direction, but doesn't achieve what the OP wants, which is to "span" the range across multiple elements (nodes), while using offsets within those elements.
The following achieve it:
var range = document.createRange();
var root_node = document.getElementById("test");
range.setStart(root_node.querySelector('a').firstChild, 2);
range.setEnd(root_node.querySelector('span').firstChild, 3);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
<div id="test">
hello
<span>world</span>
</div>
Note how on both setStart and setEnd we pass a text node - this is the firstChild of the anchor and span elements.
For further reading on this subject, please refer to this excellent explanation.
You called setStart twice, instead of setStart followed by setEnd, and you are specifying an offset of 2 in each case, but I don't think you want an offset because that puts your node index out of range (and throws an error).
var range = document.createRange();
var root_node = document.getElementById("test");
// Start at the `hello` element.
range.setStart(root_node.childNodes[0], 0);
// End in the `world` node
range.setEnd(root_node.childNodes[1], 0);
range.selectNodeContents(root_node);
let sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
<div id="test">
hello
<span>world</span>
</div>

How to select text range within a contenteditable div that has no child nodes?

I'd like to select text within a content editable div. I'd like to provide a start index and an end index.
For example if I have a div:
<div id="main" contenteditable="true">
Hello World
</div>
I'd like a function to do something like "selectText('#main',6,10)" and it would select set the focus to main and select "World".
But all the examples that I see online assume that the container div has children. But mine don't have any children. Just the text within the div.
This is what I've tried so far to no avail:
$('#main').focus();
var mainDiv = document.getElementById("main");
var startNode = mainDiv;
var endNode = mainDiv;
var range = document.createRange();
range.setStart(startNode, 6);
range.setEnd(endNode, 10);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
But I get:
Uncaught IndexSizeError: Failed to execute 'setStart' on 'Range': There is no child at offset 6.
My jsfiddle:
http://jsfiddle.net/foreyez/h4bL5u4g/
But mine don't have any children. Just the text within the div.
The text within the div is a child – it's a text node. That's what you want to target.
You will also need to trim its nodeValue to get the proper offset. Otherwise, the leading spaces will be included.
This seems to do what you want:
function SelectText(obj, start, stop) {
var mainDiv = $(obj)[0],
startNode = mainDiv.childNodes[0],
endNode = mainDiv.childNodes[0];
startNode.nodeValue = startNode.nodeValue.trim();
var range = document.createRange();
range.setStart(startNode, start);
range.setEnd(endNode, stop + 1);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} //SelectText
$('#main').focus();
SelectText('#main', 6, 10);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main" contenteditable="true">
Hello World
</div>

HTML JS Text editor

i am trying to create a very simple html text editor.
i have utilised the context menu function to have different format options once a user selects on the highlighted text on screen will have a span tag appended to it.
this is what i have.
function StyleChange(property) {
var span = document.createElement("span");
span.style.color = property;
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
this works fine for changing the colour of the highlighted text.
what i would like to do is be able to use this function to change any format of style for the text by passing an extra parameter when the function is called.
so when it is called it will say something like. StyleChange('color',red) or StyleChange('background','yellow').
something like
function StyleChange(style,property) {
var span = document.createElement("span");
**span.style. + style = property;**
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
i get an error message with this any ideas?
Square brackets are used to pass properties, like:
function StyleChange(property, value){
var span = document.createElement('span');
span.style[property] = value;
if(window.getSelection){
var sel = window.getSelection();
if(sel.rangeCount){
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
You probably want
span.style += property;
instead.
Another consideration: you don't want to use the name property for your variable, since that's a reserved keyword (as you can see since it's highlighted in blue) and Bad Things™ will happen if you use one.

js/jquery: contenteditable, insert text and move cursor to end

I need to insert text into a contenteditable div and then have the cursor be at the end of the inserted text.
I got the solution below from here Insert text at cursor in a content editable div.
That works great if the text is added to am empty div. But it does not work if the user has already typed in text. Or, if the function is used to insert text, the user then places the cursor somewhere inside the newly inside text, and the function is then called again. Then the cursor is left at the beginning of the inserted text.
EDIT: The code below works in IE, properly setting the cursor, but has the problem in Chrome.
function insertTextAtCursor(text) {
var sel, range, html;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode( document.createTextNode(text) );
}
} else if (document.selection && document.selection.createRange) {
document.selection.createRange().text = text;
}
}
Alright, once I realized it was a Chrome/IE thing I managed to find the answer tucked away inside one of the comments to the answer that I first found.
function insertTextAtCursor(text) {
var sel, range, html;
sel = window.getSelection();
range = sel.getRangeAt(0);
range.deleteContents();
var textNode = document.createTextNode(text);
range.insertNode(textNode);
range.setStartAfter(textNode);
sel.removeAllRanges();
sel.addRange(range);
}

How to remove all nodes from selected ranges

For example, I have such text:
Some test <span> in span1</span> sss <span>in span2</span> end of text.
when I select " test in span1 sss in spa" I want to just delete parent spans of selected ranges and create new range
that will contain my selected text.
Some<span> test in span1 sss in spa</span>n2 end of text.
I'm using window.getSelection(),range,nodes
Please help!
You can do this with the deleteContents(), toString() and insertNode() methods of a range obtained from the selection.
The following will work in all major browsers except IE <= 8. You'll need a different approach for those browsers, which I can outline if you need it.
Demo: http://jsfiddle.net/HUm2K/
Code:
var sel = window.getSelection();
if (sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
var newSpan = document.createElement("span");
var selectedTextNode = document.createTextNode( range.toString() );
newSpan.appendChild(selectedTextNode);
range.deleteContents();
range.insertNode(newSpan);
range.selectNode(newSpan);
sel.removeAllRanges();
sel.addRange(range);
}

Categories