javascript contenteditable: set cursor at character offset - javascript

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.

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>

Add tags around selected text in an element

How can I add <span> tags around selected text within an element?
For example, if somebody highlights "John", I would like to add span tags around it.
HTML
<p>My name is Jimmy John, and I hate sandwiches. My name is still Jimmy John.</p>
JS
function getSelectedText() {
t = (document.all) ? document.selection.createRange().text : document.getSelection();
return t;
}
$('p').mouseup(function(){
var selection = getSelectedText();
var selection_text = selection.toString();
console.log(selection);
console.log(selection_text);
// How do I add a span around the selected text?
});
http://jsfiddle.net/2w35p/
There is a identical question here: jQuery select text and add span to it in an paragraph, but it uses outdated jquery methods (e.g. live), and the accepted answer has a bug.
I have a solution. Get the Range of the selecion and deleteContent of it, then insert a new span in it .
$('body').mouseup(function(){
var selection = getSelectedText();
var selection_text = selection.toString();
// How do I add a span around the selected text?
var span = document.createElement('SPAN');
span.textContent = selection_text;
var range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(span);
});
You can see the DEMO here
UPDATE
Absolutly, the selection will be delete at the same time. So you can add the selection range with js code if you want.
You can simply do like this.
$('body').mouseup(function(){
var span = document.createElement("span");
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
});
Fiddle
Reference Wrapping a selected text node with span
You can try this:
$('body').mouseup(function(){
var selection = getSelectedText();
var innerHTML = $('p').html();
var selectionWithSpan = '<span>'+selection+'</span>';
innerHTML = innerHTML.replace(selection,selectionWithSpan);
$('p').html(innerHTML);
});
and In your fiddle you are again opening a new <p> instead of a closing </p>. Update that please.
THIS WORKS (mostly*)!! (technically, it does what you want, but it needs HALP!)
JSFiddle
This adds <span ...> and </span> correctly, even if there are multiple instances of the selection in your element and you only care about the instance that's selected!
It works perfectly the first time if you include my commented line. It's after that when things get funky.
I can add the span tags, but I'm having a hard time replacing the plaintext with html. Maybe you can figure it out? We're almost there!! This uses nodes from getSelection. Nodes can be hard to work with though.
document.getElementById('d').addEventListener('mouseup',function(e){
var s = window.getSelection();
var n = s.anchorNode; //DOM node
var o = s.anchorOffset; //index of start selection in the node
var f = s.focusOffset; //index of end selection in the node
n.textContent = n.textContent.substring(0,o)+'<span style="color:red;">'
+n.textContent.substring(o,f)+'</span>'
+n.textContent.substring(f,n.textContent.length);
//adds the span tag
// document.getElementById('d').innerHTML = n.textContent;
// this line messes stuff up because of the difference
// between a node's textContent and it's innerHTML.
});

How do I select (get range or selection object) an element in a contenteditable div if I know the element ID?

I have this contenteditable element:
<div id="editMe" contenteditable="true">
There is some text here.
<span id="selectThisText">This is the target text.</span>
And some here.
</div>
I want to use Javascript to select (get range object) the contents of #selectThisText. How do I get the range of the content in that element?
Thanks in advance!
Create a range and use its selectNodeContents() method.
var span = document.getElementById("selectThisText");
var range = document.createRange();
range.selectNodeContents(span);
This doesn't work in IE <= 8, which doesn't support DOM Range. However, this is one case which is just as easy in old IE:
var span = document.getElementById("selectThisText");
var textRange = document.body.createTextRange();
textRange.moveToElementText(span);
//get Selection
var selection = window.getSelection();
//get Range
var range = selection.getRangeAt(0); //where the range selection happens.
var text = $('#selectThisText').text();
Demo: http://jsfiddle.net/5NBnP/

html - selection range - getting the range + starting node + ending node + distance

From my previous question for selecting specific html text, I have gone through this link to understand range in html string.
For selecting a specific text on html page. We need to follow this steps.
Assumed HTML:
<h4 id="entry1196"><a
href="http://radar.oreilly.com/archives/2007/03/call_for_a_blog_1.html"
class="external">Call for a Blogger's Code of Conduct</a></h4>
<p>Tim O'Reilly calls for a Blogger Code of Conduct. His proposals are:</p>
<ol>
<li>Take responsibility not just for your own words, but for the
comments you allow on your blog.</li>
<li>Label your tolerance level for abusive comments.</li>
<li>Consider eliminating anonymous comments.</li>
</ol>
java script to make selection by range
var range = document.createRange(); // create range
var startPar = [the p node]; // starting parameter
var endLi = [the second li node]; // ending parameter
range.setStart(startPar,13); // distance from starting parameter.
range.setEnd(endLi,17); // distance from ending parameter
range.select(); // this statement will make selection
I want to do this in invert way. I mean, assume that selection is done by user on browser (safari). My question is that How can we get starting node (as we have 'the p node' here) and ending node (as we have 'the second li node' here) and the range as well (as we have 13,17 here)?
Edit : my efforts (From this question)
var sel = window.getSelection();
if (sel.rangeCount < 1) {
return;
}
var range = sel.getRangeAt(0);
var startNode = range.startContainer, endNode = range.endContainer;
// Split the start and end container text nodes, if necessary
if (endNode.nodeType == 3) {
endNode.splitText(range.endOffset);
range.setEnd(endNode, endNode.length);
}
if (startNode.nodeType == 3) {
startNode = startNode.splitText(range.startOffset);
range.setStart(startNode, 0);
}
But, yet I am confused about getting like, if selected is first paragraph or second or third, or selected is in first heading or second heading or what....
Storing the selected range is simple. The following will return only the first selected range (Firefox at least supports multiple selections):
<script type="text/javascript">
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.selection.createRange();
}
return null;
}
var range;
</script>
<input type="button" onclick="range = getSelectionRange();"
value="Store selection">
range will have properties startContainer (the node containing the start of the range), startOffset (an offset within the start container node: a character offset in the case of text nodes and child offset in elements), endContainer and endOffset (equivalent behvaiour to the start properties). Range is well documented by its specification and MDC.
In IE, range will contain a TextRange, which works very differently. Rather than nodes and offsets, TextRanges are concerned with characters, words and sentences. Microsoft's site has some documentation: http://msdn.microsoft.com/en-us/library/ms533042%28VS.85%29.aspx, http://msdn.microsoft.com/en-us/library/ms535872%28VS.85%29.aspx.

Categories