Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cursor moves to end of the contenteditable div when character removed from div

I am working on creating my own chat application in which I used content-editable div as chatbox. I am implementing mention feature where my mention is username wrapped in a span tag followed by a space( ) but my problem is when I remove space my cursor moves to the end of the div.

with space

<html>

<head>
</head>

<body>
  <div contenteditable=true><span contenteditable='false'>UserName</span>&nbsp;</div>
</body>

</html>

without space

<html>

<head>
</head>

<body>
  <div contenteditable=true><span contenteditable='false'>UserName</span></div>
</body>

</html>

This is how I implemented in code and also this bug happens only in chrome.

like image 225
Himanshu Bansal Avatar asked Mar 08 '18 07:03

Himanshu Bansal


People also ask

Why is my contentEditable caret jumping to the end in Chrome?

The problem is that your contenteditable element is a div , which by default is display: block . This is what causes your caret position problem. This can be fixed by making the outermost div non-editable and adding a new editable div that will be treated as inline-block .

How do you move the cursor to the end of contentEditable entity?

moveToElementText(contentEditableElement);//Select the entire contents of the element with the range range. collapse(false);//collapse the range to the end point. false means collapse to end rather than the start range. select();//Select the range (make it the visible selection } } }( window.

How to set the caret cursor position in a contentEditable element div)?

In order to set caret cursor position in content editable elements like div tag is carried over by JavaScript Range interface. The range is created using document. createRange() method.

How do you use contentEditable in react?

contentEditable prop in ReactWhen you add contentEditable=true to a component you see a warning from React: Warning: A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated.


3 Answers

This is actually a bug in the browsers, I have a workaround but I don't know how much it will help you but this is the best we can do now in my knowledge.

div>span {
   display:inline-block;
}
<div contenteditable="true"><span contenteditable="true"><span contenteditable="false">UserName</span></span>&nbsp;</div>

Without space

div>span {
  display:inline-block;
}
<div contenteditable="true"><span contenteditable="true"><span contenteditable="false">UserName</span></span></div>

If you want a full proof content to be there then here is a solution with script combining : before pseudo:

Here am taking the width of the : before pseudo-element and hading it over to the div element padding-left. I have used script keeping in mind of your dynamic usernames.

var testBox = document.querySelector('.test'),
  pseudoBeforeWidth = window.getComputedStyle(testBox, ':before').width, //getting the width of the parent elemnts `:before`
  pseudoBeforeContent = window.getComputedStyle(testBox, ':before').content;

testBox.style.paddingLeft = pseudoBeforeWidth; //handing the width to the parent elemnts padding left style
var i = 0
var n = 0;
testBox.addEventListener('keydown', function(event) {
  const key = event.key; // const {key} = event; ES6+
  if ((key === "Backspace" || key === "Delete") && n == 0) {
    i++
    console.log(testBox.textContent, i, n);
    if (testBox.textContent == "" && i >= 2) {
      testBox.className += ' userhide ';
      testBox.style.paddingLeft = 0;
      n = 1;
    }
  }
});
div:before {
  content: attr(data-user);
  display: inline-block;
  position: absolute;
  left: 5px;
}

.test.userhide:before {
  display: none;
}
<div class="test" contenteditable="true" data-user="UserName"></div>

I hope this was helpful for you.

like image 154
Jithin Raj P R Avatar answered Oct 24 '22 15:10

Jithin Raj P R


Analysis

Think of a simple text editor(notepad) where you have typed three characters - TRY

Now, its obvious that the cursor can be placed only at 4 places here, which are:

  • before T
  • between T and R
  • between R and Y
  • after Y

The reason for which is simply because, these 3 characters are the only editable ones here and they are 3 individual entities, which can't be edited/separated further. For example, we can't place the cursor in between the letter 'T' and separate the and from it.

Same is the case with your scenario as the cursor is moved to the end of the div once the &nbsp; is cleared because the contenteditable=true is set to the <div> and is set as false to the <span>. Therefore the div is the only editable element here and the span is not. So the cursor can position itself only on an editable place which is either at the beginning of an editable element(div in this case) or at the end of it and not in between.

As the div is a block element, the cursor is moved to the end of the page as the block elements occupy the whole width of its parent. We can see the cursor being moved to the middle of the page if we set width: 50%; to the div.

Solution

Considering your comments on other answers, that you need to keep the width as 100%, the solution is to wrap the div with another one which also have the attribute contenteditable=true and keep the child div as an inline-block element.

<div contenteditable=true>
  <div class="inl-blck" contenteditable=true><span contenteditable='false'>UserName</span>&nbsp;</div>
</div>

CODEPEN

Explanation

Here, both the parent and child divs are editable where the parent occupying the full width (100%) and the child is an inline-block element, which just occupies the space of its contents. Therefore, once the &nbsp; is cleared, the cursor moves to the end of the next editable content which is the child div.

Note that, you need to keep the child div and span along with the &nbsp; in the same line in you code as otherwise it will create extra characters (whitespaces) in the section, which will break the contenteditable further.

Hope this helps.

like image 42
Gibin Ealias Avatar answered Oct 24 '22 13:10

Gibin Ealias


This Can Help You

<div contenteditable=true style="display: inline-block;background: #000;color:#fff;"><span contenteditable=false>UserName</span>&nbsp;</div>
like image 30
Manish Avatar answered Oct 24 '22 14:10

Manish