Inserting caret after an inserted node - javascript

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

Related

Using JS to set cursor in a contenteditable div works fine in Chrome, but not if the div is empty

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

Select last word or specific range of Contenteditable

I've been trying to select only the last chars of a ContentEditable Element
I've used an example from Mozilla on http://jsfiddle.net/mh7HK/ which works on an input field but not on a ContentEditable field, although, selecting all does work
This works:
var elem = document.getElementById("contentEdit");
var range = document.createRange();
range.selectNodeContents(elem);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
This doesn't:
var elem = document.getElementById("contentEdit");
var range = document.createRange();
range.setStart(elem, 5) // breaks range Object?
range.setEnd(elem, 8) // breaks range Object?
range.selectNodeContents(elem);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
The .setStart() and .setEnd() should work like that, right?
I've looked at the limited Documentation of jQuery carrot, jQuery selection, Rangy etc., but they all seem to be aimed at getting the selection not setting it.
Two problems:
selectNodeContents(node) moves the range to encompass the whole content of node, so your setStart() and setEnd() calls are essentially ignored.
setStart(node, offset) moves the range's start boundary to offset offset within node. In the case of element, that means placing the boundary after offset child nodes of the element. In the case of a text node, which is probably what you want, the range boundary is placed after offset characters. Same with setEnd(). If you have a single text node within your editable element, you're probably done. Once you start have nested elements, you may want a more subtle approach if you want to set the selection relative to your editable element's text content.
See also https://stackoverflow.com/a/24117242/96100.

How to set caret to the last character of Range object in contenteditable?

I'm having hard time figuring out how to set the caret position based on the last character in the Range object.
For instance I have contenteditable which has some text.
Hello world Test
Then I highlight portion of text ("wor") with mouse:
Then mouseup event will get selected range.
After focus it gone, the selected highlight will disappear.
Now, my goal is to set the caret to between "r" and "l" like image below (the end of the Range)
So far I got getting selection and restoring part worked but can't figure out the caret setting based on Range object.
Code:
var node = this.element.find(".reContentArea")[0];
var range = document.createRange();
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
You can use focusOffset to get the character position where the selection ends:
$('.reContentArea').on('mouseup', function() {
var offset= window.getSelection().focusOffset
})
In setting the caret position on a contenteditable element we can use setStart(startNode, startOffset). As you can see in this method we need to specify the node and the offset within that node.
var editable = $('.reContentArea')[0],
range = document.createRange(),
sel = window.getSelection();
range.setStart(editable.firstChild, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
It's not clear to me what even you want to trigger the "to set the caret to", but I've made a simple click event with this solution on this jsfiddle.
I would recommend using this awesome plugin
https://github.com/acdvorak/jquery.caret
// Select everything after the 6th character
$('input').range(6);
$('textarea').val('Hello\nWorld').range(6).range().text === 'World';
check demo here :
http://www.examplet.org/jquery/caret.php

Move cursor to placeholder element in a contentEditable DIV

I have a contentEditable DIV and, when the user presses a key, the underlying code is processed and replaced by updated code. Alas, this causes the cursor position to be lost.
However, in order to preserve the cursor position, I am successfully inserting a <span id="placeholder"></span> into the DIV at the correct position before processing begins. This preserves the cursor's intended position, but now I can't seem to set the range to select it.
Here's what I currently have:
function focusOnPlaceholder() {
var placeholder = document.getElementById('placeholder');
if( !placeholder ) return;
var sel, range;
if (window.getSelection && document.createRange) {
range = document.createRange();
range.selectNodeContents(placeholder);
range.collapse(true);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(placeholder);
range.select();
}
}
Any help would be appreciated, and a cross-browser solution would be incredible :)
A cross-browser solution would be to use my Rangy library and specifically the selection save/restore module, which uses a similar placeholder technique and is well tested. However, this can probably be fixed without using a library by putting some content (for example, a non-breaking space (\u00A0 or in HTML) inside your placeholder element. You may want to remove the placeholder in focusOnPlaceholder() after selecting the range.

innerText polyfill for Firefox

In WebKit innerText seems to return the text of the element that the user sees which is exactly what I need.
Are there any polyfills for Firefox?
For example:
<div id='1'><br> f<div style='display:none'>no</div>oo bar</div>
<script>
function test(){ return document.getElementById('1').innerText }
</script>
The function test would return "\n foo bar".
The goal is to make an editable text area where links are clickable and where tags are highlighted and where the linking and highlighting is created on the fly while typing.
My approach is:
On every keyup:
save the cursor position
cut the text with innerText
parse the links and tags of the text returned by innerText
paste the parsed text into the editable area
restore the cursor position
Thanks!
You can use the toString() method of the Selection object in Firefox, which acts a lot like innerText. Since you're already saving the cursor position before extracting innerText in your example, the following does not bother to save and restore the selection, but otherwise you should be doing that.
function getInnerText(el) {
var text = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.selectNodeContents(el);
sel.addRange(range);
text = sel.toString();
sel.removeAllRanges();
}
return text;
}
Firefox may implement innerText since Aryeh Gregor is writing a specification for innerText.
See http://aryeh.name/spec/innertext/innertext.html and http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2011-February/030179.html

Categories

Resources