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>
Related
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);
I have an HTML structure like this:
<div contenteditable="true">This is some plain, boring content.</div>
I also have this function that allows me to set the caret position to anywhere I want within the div:
// Move caret to a specific point in a DOM element
function SetCaretPosition(object, pos)
{
// Get key data
var el = object.get(0); // Strip inner object from jQuery object
var range = document.createRange();
var sel = window.getSelection();
// Set the range of the DOM element
range.setStart(el.childNodes[0], pos);
range.collapse(true);
// Set the selection point
sel.removeAllRanges();
sel.addRange(range);
}
This code works completely fine until I start adding child tags (span, b, i, u, strike, sup, sub) to the div e.g.
<div contenteditable="true">
This is some <span class="fancy">plain</span>, boring content.
</div>
Things get more complicated when these child tags end up with child tags of their own e.g.
<div contenteditable="true">
This is some <span class="fancy"><i>plain</i></span>, boring content.
</div>
Essentially, what happens, is that setStart throws an IndexSizeError when I try to SetCaretPosition to an index higher than the start of a child tag. setStart only works until it reaches the first child tag.
What I need, is for the SetCaretPosition function to handle an unknown number of these child tags (and potentially an unknown number of nested child tags) so that setting the position works in the same way it would if there were no tags.
So for both this:
<div contenteditable="true">This is some plain, boring content.</div>
and this:
<div contenteditable="true">
This is <u>some</u> <span class="fancy"><i>plain</i></span>, boring content.
</div>
SetCaretPosition(div, 20); would place the caret before the 'b' in 'boring'.
What is the code I need? Many thanks!
So, I was experiencing the same issue and decided to write my own routine quickly, it walks through all the child nodes recursively and set the position.
Note how this takes a DOM node as argument, not a jquery object as your original post does
// Move caret to a specific point in a DOM element
function SetCaretPosition(el, pos){
// Loop through all child nodes
for(var node of el.childNodes){
if(node.nodeType == 3){ // we have a text node
if(node.length >= pos){
// finally add our range
var range = document.createRange(),
sel = window.getSelection();
range.setStart(node,pos);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return -1; // we are done
}else{
pos -= node.length;
}
}else{
pos = SetCaretPosition(node,pos);
if(pos == -1){
return -1; // no need to finish the for loop
}
}
}
return pos; // needed because of recursion stuff
}
I hope this'll help you!
If you are going to position the caret based on the character index of the content editable, the easiest way would be using modify method:
const position = 6;
const el = document.querySelector('#editable');
const selection = document.getSelection();
if (!selection || !el) return;
// Set the caret to the beggining
selection.collapse(el, 0);
// Move the caret to the position
for (let index = 0; index < position; index++) {
selection.modify('move', 'forward', 'character');
}
No need to traverse the child nodes.
It only work for object Text childNodes(0).So you have to make it.Here is not so very standard code,but works.Goal is that (p) id of (we) will output object text.If it does then it might work.
<div id="editable" contenteditable="true">dddddddddddddddddddddddddddd<p>dd</p>psss<p>dd</p><p>dd</p>
<p>text text text</p>
</div>
<p id='we'></p>
<button onclick="set_mouse()">focus</button>
<script>
function set_mouse() {
var as = document.getElementById("editable");
el=as.childNodes[1].childNodes[0];//goal is to get ('we') id to write (object Text)
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el, 1);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
document.getElementById("we").innerHTML=el;// see out put of we id
}
</script>
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>
I need to set the cursor at a specific character offset. In the example below, I'd want to, for example, set it between the "a" and the "b".
<ul contenteditable = true >
<li id = "test">abcdef</li>
</ul>
I asked before and got this fiddle: http://jsfiddle.net/5a9uD/1/
That worked great for the given example and for what I needed it for then. But it does not work with this example: http://jsfiddle.net/mdwWN/ It gets an IndexSizeError at
range = sel.getRangeAt(0);
You just have to pass text container's childNodes[0] to range.setStart function.
Check this out.
function setSelectionRange(aNode, childElem, aOffset) {
aNode.focus();
var sel = window.getSelection(),
range = sel.getRangeAt(0);
range.collapse(true);
range.setStart(childElem.childNodes[0], aOffset),
sel.removeAllRanges();
sel.addRange(range);
}
var container = document.getElementById("test");
var childElement = document.getElementById('item1');
setSelectionRange(container, childElement, 1);
Here is the working fiddle.
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);
}