I can insert html into a contenteditable div just fine using this code:
function insertHtmlAtCursor(html) {
let selection, range, node;
selection = window.getSelection();
if (selection.getRangeAt && selection.rangeCount) {
range = window.getSelection().getRangeAt(0);
node = range.createContextualFragment(html);
range.insertNode(node);
}
}
However, afterwards the cursor is placed within the inserted html. Instead I need it to be positioned just after the inserted html. Any ideas?
It seems like https://developer.mozilla.org/en-US/docs/Web/API/Range/setEndAfter e.g. range.setEndAfter(node); range.collapse(); might do. Or even https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapseToEnd selection.collapseToEnd();.
Related
I have been trying to build a web based text editor. And as part of the process, I am trying to dynamically create and modify elements based and keystroke events for font editing. In this particular jsfiddle example I'm trying to create a strong element upon hitting CTRL+b and setting the focus/caret inside the strong element so that subsequent text entered will be part of the bold element and hence will have bold text. But my code is just creating a strong element but not transferring the focus hence no text is getting bolder.
In the below code I'm creating event listener to capture keystroke events
p=document.getElementsByTagName("p")[0];
//console.log(p)
// adding eventlistener for keydown
p.addEventListener("keydown",listener);
// eventlistenerr callback function
function listener(){
e=window.event;
if(e.ctrlKey && e.keyCode==66)
{
console.log("CTRL+B");
// creating bold element
belm=document.createElement("strong");
belm.setAttribute("contenteditable","true")
p.appendChild(belm);
//bug below
// setting focus inside bold element
setfocus(belm,0);
e.preventDefault();
}
}
Here is the function for setting the focus.
function setfocus(context, position){
var range = document.createRange();
position =position || 0;
var sel = window.getSelection();
range.setStart(context, position);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
context.focus();
}
However, I have not doubt that the function which sets focus is faulty, because in the fiddle if you observe, I have created a separate setup just to test this
out. Click on the button "Click Here" and the focus dynamically shifts to paragraph element without any hassle. I am unable to figure out what is going wrong.
It's pretty much impossible to move the cursor into an empty element in a contenteditable div. However, as shay levi suggested in another post, you can insert the zero-width character ÈB into your empty element to give it an index value.
Here's an example*:
function insertNode(nodeName) {
var sel = window.getSelection(),
range;
range = sel.getRangeAt(0);
range.deleteContents();
var child = document.createElement(nodeName);
child.innerHTML = '';
range.insertNode(child);
}
var div = document.querySelector('div'),
btn = document.querySelector('button');
btn.addEventListener('click', function() {
insertNode('strong');
div.focus();
});
div.focus();
<div contenteditable></div><button><strong>B</strong></button>
*For the sake of simplicity, this script doesn't toggle bold text, it only sets it.
Sample of what I mean:
http://jsfiddle.net/vXnCM/3717/
<div id="editable" contenteditable="true">
text text text text <span id="test" style="color:red"></span>text text
</div>
<button id="button" onclick="setCaret()">focus</button>
<script language="javascript">
function setCaret() {
var el = document.getElementById("test");
var range = document.createRange();
var sel = window.getSelection();
range.selectNodeContents(el);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
el.focus();
}
</script>
Open the link in Chrome and click Focus, then start typing. Your new text is red.
But if you delete the "ASDF" from the HTML and run it again, your new text is black.
How can I have it be red with no starting text in the div?
(Or, equally effective, how can I have it be red and then clear the starting text from the div without losing the cursor location?)
This is a common problem. Here is some background and potential solutions (none of them are ideal):
js contenteditable - prevent from writing into newly inserted element
How to place caret inside an empty DOM Element Node
How to select a node in a range with webkit browsers?
Set cursor after span element inside contenteditable div
I'm not sure if this is what you want, but I think so, basically it's your range selection logic that needs to be revised. I don't delete the text, but highlight it so when the user types the text is removed, which I think might be what you want (a la 'start typing here').
function setCaret() {
var el = document.getElementById("test");
var range = document.createRange();
var sel = window.getSelection();
range.setStartBefore(el);
range.setEndAfter(el);
sel.addRange(range);
el.focus();
}
Checkout the forked version here:
http://jsfiddle.net/uhe76wup/
Good reference to learn about Range:
https://developer.mozilla.org/en-US/docs/Web/API/Range
So I have a method that takes a tag and wraps the selected text in that tag.
function wrap(tag)
{
var sel, range;
if (window.getSelection)
{
sel = window.getSelection();
if (sel.rangeCount)
{
range = sel.getRangeAt(0);
var selectedText = range.toString();
range.deleteContents();
range.insertNode(document.createTextNode('['+tag+']'+selectedText+'[/'+tag+']'));
}
}
}
This issue with this however, is after it's done wrapping the text and inserting the node the caret (where they are typing) is placed BEFORE the inserted text.
Is there such way to insert the text and have the caret remain at the end of it?
Please note i'd prefer if this could be done without the use of jQuery or any other library. I only need it to work in webkit (Safari).
You can use the range.setStartAfter and range.setEndAfter methods to set the start and end points to the point directly after your new node. I setup a jsfiddle example here: http://jsfiddle.net/phil_mcc/tM3mA/
//move the caret
range.setStartAfter(newNode);
range.setEndAfter(newNode);
sel.removeAllRanges();
sel.addRange(range);
do this after inserting the node to the range
range.collapse(false);
this will change position of selection range to the end of the range, so my guess is it should set the cursor at end position
I need to get the position of the selected text within a content non-editable div (not a textarea, not a rtf editor, just a simple div)
I want to do this in order to enable users to select pieces of an article and "highlight it", by wrapping it in a span with a different background and, of course, an article is build with divs and/or p-s etc, not textareas or rtfs
Any ideas?
P.s. You can also use jQuery :D
P.s.s. I need the position of the selection, not the selection itself. Aka: it start from index I to index J. I need this because the normal method of finding the text in the parent does not always return a unique result, which would suck :)
If you just want to change the background of the selected text, the easiest way to do this is by using document.execCommand(). See my answer here: Change CSS of selected text using Javascript
//Wrap selected text in span tags with the class 'hl'
//Take some action after (in this case, a simple alert)
$("p").live("mouseup",
function() {
selection = getSelectedText();
if(selection.length >= 3) {
$(this).html($(this).html().replace(selection, $('<\/span>').attr({'class':'hl'}).html(selection).parent().html()) );
alert(selection);
}
}
);
//Grab selected text
function getSelectedText(){
if(window.getSelection){
return window.getSelection().toString();
}
else if(document.getSelection){
return document.getSelection();
}
else if(document.selection){
return document.selection.createRange().text;
}
}
Code comes from here: http://esbueno.noahstokes.com/post/92274686/highlight-selected-text-with-jquery
You can check if text is selected by running :
window.getSelection and document.getSelection() and document.selection
(because browsers can check this i different ways)
and then search for div containing this text .
For getting the position of the selection, try these links:
http://bytes.com/topic/javascript/answers/153164-return-selectionstart-div
Set cursor position on contentEditable <div>
Well, even though you found a solution to the problem stated in your 2nd paragraph, i don't think the answer to your main question has been given. :)
The object Selection has a property named anchorOffset, giving exactly what you asked for (the position of the selected text within an element). The above link will tell you about which browsers support it, i'm afraid IE <9 might not.
function show_selected()
{
var sel = selection();
console.log(sel.anchorOffset + ':' + sel);
}
Now if you bind show_selected to, say, mouseup, you will see the offset and the selected text printed on the js console.
The fonction selection may be the following, supposed to be cross-browser:
function selection()
{
var sel;
if(window.getSelection){
sel = window.getSelection()
}
else if(document.getSelection){
sel = document.getSelection()
}
else if(document.selection){
sel = document.selection.createRange()
}
return sel
}
I have a UIWebView i want to introduce functionality of content(may be text or text and images) selection so that the user can email it.
Is there any way to get the HTML code for the given selection using JavaScript?
I tried the built-in clipboard of webkit but its seems not working for images selection.May be i am wrong,if there is a way please tell me.
var range, frag, sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
frag = range.cloneContents();
}
This will give you a DocumentFragment containing the selected content. You can traverse the descendants of the fragment using the usual DOM methods. If you must have a literal HTML string, you could then do the following:
var div = document.createElement("div");
div.appendChild(frag);
alert(div.innerHTML);
Note that this last part won't work if the selected contents can't be placed inside a <div> (if, say, the whole body or document was selected).