How can I get the current caret position in tinyMCE? Referring to this question "Get cursor position or number of line on which the cursor is in TinyMCE" there is mentioned how to get the line number in tinyMCE, however I can't find no reference on how to get the actual caret position? I need this since I am using tinyMCE as a document for real-time collaboration therefore I need to know the position of the caret for clients writing in the same document so that I broadcast the position on some events (keystroke, click, etc..) and paint custom carets on the position for one client to know where the other client is writing or editing.
The only solution I found was to add a span after the content, select it and remove it as follows:
<span id="caret_pos_holder"></span>
Then once inserted, do this...
ed.selection.select(ed.dom.select('span#caret_pos_holder')[0]); //select the span
ed.dom.remove(ed.dom.select('span#caret_pos_holder')[0]); //remove the span
Is this the only way around it?
EDIT: Actually the above only sets the position of the cursor to the end of the content, something which I already took care of using bookmarks.
Is it possible that there is nothing that can get the cursor's position?
var sel = ed.selection.getSel();
will return the current selection as a selection object. You can then query sel.anchorNode and sel.anchorOffset to get current caret position.
Broadcasting this data to the other client(s) would be easier than trying to extract the position as a number, but if that's really necessary, you could climb the DOM from the position given above.
Related
I'm building an Editor that does syntax highlighting on code. My current approach is to just take the text in the editor (a div with contenteditable set to true) and check for regex matches in the string. I then replace the matches with <span> elements and apply some styles to those span elements. Then I replace the complete text in the divider with the new text using the .innerHTML attribute. This works just fine, but I have to type backwards, because upon inserting the text, my cursor is reset to position zero. I tried recording the value of selectionStart before inserting and then doing
element.selectionStart = oldSelectionStart + 1;
but it didn't work. I assume this is because of chrome's render pipeline, where JavaScript is run before rendering the page, and the cursor is reset upon render, not upon set... Can anyone help? How do I manage to keep the cursor where it was?
element.selectionStart works for input elements, not for contentEditable elements. You should try creating a Range object, set its startContainer & startOffset and collapse it to set caret at the required position. Refer to [https://developer.mozilla.org/en-US/docs/Web/API/Range] for details.
PS: If you would like to set your cursor to the 'end of line', you could do it easily with range.selectNode(requiredNode) and range.collapse(true) to move the caret to the end of node, i.e., line
I have a contenteditable div
<div contenteditable="true" class="editable"></div>
I append some html to it using :
$('.editable').html('<p><br></p>');
How I can set the caret before <br> ?
Setting cursor position in content editables is tricky business. The handling of cursor is browser dependent.
There are range and selection apis which are quite inconsistent over different browsers.
This is how to play with cursors:
Create a range object by document.createRange()
select the specified position by range methods (see the api)
Get selection object by selection=window.getSelection()
remove all existing selection by selection.removeAllRanges();
add the range to the selection by selection.addRange(range);
In this situations there is no text content in paragraph element, so you cannot place cursor inside it. You can either select the node by range.selectNode or you can place the cursor before/after the element.
Placing the cursor before or after will also behave weirdly in different browsers especially in edge cases (like in empty div or first or last postion).
You have to clearly specify what you want to achieve in the UI. Then I can suggest some logic to accomplish that.
I am trying to create a richtext area using contenteditable. The parser for the highlighting returns a html string which I replace the content of the element with (perhaps ineffective, thats the way it has to be for now).
The problem with contenteditable is that the cursor position is not retained after something like this, which I can understand.
Is there a way to retain this position? I could make the parser return a element representing where the caret should be (given the cursors offset in the text) if this would aid in a solution.
Thanks in advance.
I could make the parser return a element representing where the caret should be
That would be enough. Suppose you parse the generated HTML and now you have a reference to the caret-should-be-here element in sentinel.
Use document.createRange() to create a DOM range. Let here be the new range. Call here.selectNode(sentinel) to have the range surround the element.
Call here.deleteContents() to remove the dummy element.
Call window.getSelection().removeAllRanges() to clear the current selection, which has gotten messed up by the HTML change.
Call window.getSelection().addRange(here) to put the cursor where the element used to be.
is sentinel my anchor element?
Yeah, I guess.
How do I fetch the cursor position in the string? ... I want the offset from the start of the string.
Let's start with the cursor position. The zeroth range of the window's selection should be the cursor's position. That is, the range's start and end are in the same place, at the cursor. However, these locations are expressed in a way that's geared toward DOM trees, rather than for string and offsets. They have a (start|end)Container and a (start|end)Offset. Check the DOM spec for what these mean.
You're interested in some sort of string offset. As I interpret it, this would be like, if you took a plaintext-only version of the subtree, what index corresponds to the range? There are multiple ways to define a plaintext version of a subtree. One is what the textContent property returns, where foo<br>bar gives "foobar". Some browsers define innerText, where foo<br>bar gives "foor\nbar". You've probably picked the one that you'll be using, but it's not stated in the question.
Anyway, here's an idea, but it might not be the right kind of offset for your app.
Set the window selection to a single range going from the beginning (wherever index 0 should be) to the cursor position.
Read window.getSelection().toString().length. In the browsers I've developed for, toString on a selection object gives results comparable to innerText.
I have an UIWebView with a huge book in it. I'm changing it's font size via javascript, using "document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust='150%';
Html-page gets larger, but the scroll position remains the same, causing text to shift out of a users sight.
The only idea that I have, is really weird and inefficient:
Wrap every word in <span> tags;
find the first onscreen <span> and remember it's id;
resize font;
scroll to span, that I've found in step 2.
Is there a better way to preserve the position, that user was reading?
Finally I've found an acceptable way:
Before changing font size I use a little javascript to find and store a position of a first letter on a page:
var range = document.caretRangeFromPoint(0,0); // get a range of a first onscreen letter
var textContainer = range.startContainer.parentNode;// get an element to which it belongs
var path = getElementXPath(textContainer); // get an XPath for that element (this function is not biult in, but you can find it in some other question)
path+='|'+range.startOffset; // stick XPath and index of the letter together
After that I change the font size, find needed element by XPath, insert invisible <a> right before my letter, scroll to that invisible <a>, don't forget to remove it.
Done. That is not a stragihtforward idea, but at least it works and does not consume to much of CPU or RAM, like the idea that I'have explained in original question.
Here is the place to get getElementXPath() function
Say I have the following text:
this is some example c|ode
The pipe symbol is where the cursor is in a nicEditor div. nicEditor has a function to get the position of the cursor "getRng()".
However getRng() doesn't get the position of the cursor including HTML, and as far as I can see no such function exists (correct me if I'm wrong).
The code in the nicEditor div might actually be:
<span style='font-weig|ht: bold;'>this i</span>s some example code
You can see my problem. I'm trying to insert something into the nicEditor div and it gets inserted into the middle of a HTML tag.
My question is this: Is there a way to convert the position of the cursor with the HTML tags included? Or is there any other alternative solution?
I worked it out in the end, it wasn't an easy solution. I had to write a state machine which has four states "Text"=>"HTML"=>"String"=>"Character". Basically I wrote jQuery plugin which takes the string and the position and shifts accordingly.