Managing No Man's Land in a content editable editor - javascript

Background:
My program offers smart brace completion, that is automatic addition of a ] on typing a [.
Problem:
Consider this scenario:
Notice the editor on the left where the caret is placed. As you can see in the Inspect Element on the right, the caret is placed right between two consecutive <br>s. There is no text node or element node between them. The caret belongs to neither of the <br>s. It is a no-man's land. What is surprising is that the caret belongs to the parent editor!
Above you can see range.startContainer and range.endContainer both point to the parent content editable editor div.editor.show. Also, the second print line that mentions 2 is the caret position that I got using range.startOffset. I have a faint guess that the 2 refers to one text node and one <br> that precede the caret.
What happens due to problem:
The second ] that has to be inserted after [ gets inserted at the second index in the entire div.editor, meaning right after Th at the beginning. So, after I locate my caret in the no-man's land and press [, this happens:
Question:
How am I supposed to detect and provide a fix for this no-man's land problem?
JSFiddle
Note: this does not occur in <textarea>s.

Related

Issue with cursor jumping to last line on editing textArea

Problem Statement:
I'm trying to build this component where user can enter a dollar amount, press enter and can continue to enter more stuff.
Issue:
Whenever we enter an amount say 12345 press enter and enter 20000 and
go back to edit the previous value (12345), the cursor jumps to the last line.
Root cause:
I figured that this is happening due to the getValue function in the code (Disabling this makes it perfect).
Demo:
https://codesandbox.io/s/money-input-example-forked-3m03e?file=/src/components/MoneyInputList.jsx:165-173
Can someone help me identify/fix what I'm doing wrong? I want to be able to edit any line (format it) without cursor jumping around.
Appreciate your time.
In textarea if you update the value, the cursor will be moved to the end. If you want to maintain the cursor position you can save it before changing the value property and then update it once changed.
Before modification
savedPosition = textarea.selectionEnd
After modification
textarea.selectionEnd = savedPosition
Consider the string
$ 123
Cursor is between one and two position - 3, now, If I type 4, new cursor position will be position - 4. And string will be
$ 1,423
When pasting this, old cursor position is 4(after insertion), and since the comma was added before three, the cursor position was increased by 1. You must set the cursor as 5 after doing your change.
Since react updates the textarea asynchronously, and I am unfamiliar with that I am not able to provide a solution. But save cursor position before, and keep track of the changes you make and transform cursor position accordingly, finally update cursor position once you modify text area value.

Trying to put caret (cross-browser) at the end of line on focus

Consider the following contenteditable element
<div contenteditable>
Foo bar <span contenteditable="false"></span>x</div>
DEMO
Now, the nice thing going on here is that whatever browser you take, the caret is always placed after the x if you click somewhere inside.
But, because I don't want the x at the end of the line and you remove the x the behaviour of where the caret is placed is different (safari and Edge do strange things). So, I figured that when the last line of a contenteditable ends with a span which is not editable you will have problems, so I did
<div contenteditbale>Foo Bar <span con...="false"><span></span></div>
Which actually doesn't help at all, same behaviour. It seems that the caret will not show inside an empty span
Any suggestions how to fix this?
So are you saying the caret is going inside the span, or is it a separate element? You could create a CSS Pseudo Element to position the caret where you would like it. Just use:
div::after {
content: url(caret.png);
}
Here's an updated DEMO

Contenteditable Div - Cursor position in terms of innerHTML position

I've done my research and come across these questions on StackOverflow where people asked this same question but the thing is that they wanted to get the position either in terms of x and y coordinates or column from the left. I want to know what the position of the cursor is with respect to the div's innerHTML.
For example:
innerHTML = "This is the innerHTML of the <b>div</b> and bla bla bla..."
^
Cursor is here
So the result I want for this case is 44. How to do it ?
var target = document.createTextNode("\u0001");
document.getSelection().getRangeAt(0).insertNode(target);
var position = contentEditableDiv.innerHTML.indexOf("\u0001");
target.parentNode.removeChild(target);
This temporarily inserts a dummy text node containing a non-printable character (\u0001), and then finds the index of that character within the div's innerHTML.
For the most part this leaves the DOM and the current selection unchanged, with one minor possible side effect: if the cursor is in the middle of text from a single text node, that node will be broken up into two consecutive text nodes. Usually that should be harmless, but keep it in mind in the context of your specific application.
UPDATE: Turns out you can merge the consecutive text nodes using Node.normalize().

Adding an absolute positioned element causes line break

So I have a "cursor" object created like so:
var cursor=document.createElement('span');
cursor.id="currentCursor";
cursor.innerHTML="|";
cursor.style.fontWeight="bold";
cursor.style.position = 'absolute';
cursor.style.marginLeft="-1px";
Then I add it to the page where someone clicks with this:
var selection = window.getSelection();
var currentRange = selection.getRangeAt(0);
currentRange.insertNode(cursor);
The problem I'm running into is in certain places (mainly end of lines) if the cursor object is added it creates a line break before the object. Using insertNode to move it to another area removes the line break. Also if I set the display to "none", wait for a few seconds and then set it back to "inline" the line break is removed.
This seems like maybe a browser bug in adding absolute elements, but I was wondering if someone had a workaround. I've tried setting the width to 0px but it has no effect.
Update
So if I change the cursor to
cursor.style.position = 'static';
It doesn't have random line breaks. However this causes space to be created around the element. Any way to not allow elements to create space around them?
Update 2
Added a fiddle to show the problem:
http://jsfiddle.net/Mctittles/pSg2D/1/
Original code is a bit large but I slimmed it down to highlight this problem.
If you click at the end of the smiley face and then type it causes line 33 to trigger creating a new text node. After typing a couple letters you'll see the cursor object is forced to the next line. Clicking somewhere else to move it makes the lines merge again.
If you un-comment lines 38 and 40 you'll see what I was talking about with making it initially display:none and changing it later. This time it doesn't cause a line break.
I took out some cross-browser code for fiddler, so this might only work in Chrome
However [position:static] causes space to be created around the element.
No, it doesn’t cause it – there is no actual space created “around it”, it’s just the display width of a character plus spacing in the used font, and that gives the span element itself a width that is more than the | character itself. But when you position the element absolutely, you don’t notice that, because it is taken out of the flow, so it doesn’t push the following characters to the right.
My workaround proposal: Don’t put | into the span as innerHTML, but leave it empty – and then implement the line by giving the element a border-left:1px solid. Remove position:absolute, so that it defaults to static.
Then you might probably not like the height your cursor is getting with that – but that can be fixed as well, by setting display to inline-block, and giving it a height as well.
Here, see how you like ’dem apples: http://jsfiddle.net/pSg2D/9/
You should use CSS instead. Using z-index and maybe even float would (atleast should) fix this.
Edit: Always make sure no other styles make it break line!

different caret position on firefox and IE

To recreate the problem, please visit http://jsfiddle.net/BsJ6V/ and type any character right after the opening body tag (for example, <body>h if you type the h character).
An alert box will display the caret position. When you run it in Firefox the caret position is 56, when you run it in IE it is 60. Could you please what the matter is?
EDIT::
UPDATED LINK.
The difference is because IE counts each line break within a textarea as two characters (CRLF, or \r\n) while Firefox counts it as a single LF (\n) character.
Your function won't get the right caret position in IE if there are leading line breaks. To see this, place the caret at the start of the textarea in your first jsFiddle example and press return a few time and trying typing in one of the empty lines. To fix this, you can use a function I've posted before on Stack Overflow or if you prefer a jQuery plug-in, I've created one for dealing with textarea selections: http://code.google.com/p/rangyinputs/
UPDATE
Note that jQuery's val() method normalizes this difference in line breaks between browsers (unhelpfully, in my view, since the value that gets sent to the server isn't normalized) so that line breaks are always \n. Both my plug-in and your function return a caret position relative to the textarea's raw value property, not jQuery's normalized value, so if you are manipulating the value using the caret position, you need to use $textarea[0].value instead of $textarea.val().
You can see this difference here: http://jsfiddle.net/MyR7J/2/

Categories

Resources