4

I'm surprised I'm having trouble with this and unable to find an answer. I'm trying to get the text in a contenteditable, from the start of the contenteditable to the users cursor/caret.

Here's a jsFiddle of what I've attempted (click around the contenteditable and watch console.log).

I get the caret location and then I attempt to get the content:

I tried using textContent of the contenteditable which works but if there's content like foo<br>bar it outputs foobar when ideally it should output foo\r\nbar (Note: This is for a chrome extension I have no control over the content of the contenteditable).

innerText works as expected outputting foo\r\nbar, but as can be seen in the jsFiddle once the html in contenteditable gets a little complex the caret position doesn't seem to match the location in innerText and I have trouble outputting up to the caret.

Found some code using the Range interface and modified it to meet my needs in this jsFiddle but had the same problem with <br> as textContent did.

Note: The user will continue typing as I get the content, so looking for something that doesn't break this flow.

Just looking for direction, any quick tips on what I should try?

Sean Bannister
  • 3,105
  • 4
  • 31
  • 43
  • After some research, I think the problem lies within `vanilla-caret`. Inside the `getPos` method `range.toString()` is called. `range.toString()` doesnt return newlines correctly in editable divs (https://stackoverflow.com/questions/1146730/problem-detecting-newlines-in-javascript-range-object), therefore `textContent` does work, since it doesnt return linebreaks either. I guess the way to go is: implement a caret finding solution yourself based on `selection.toString()` (look at the link above) and then use `innerText`. – acincognito Jun 17 '20 at 11:45

1 Answers1

3

In your fiddle I replaced the JavaScript content with:

document.querySelector("#edit").addEventListener("click", function(){
  var target = document.querySelector('#edit');
  var sel = document.getSelection();
  if(!sel.toString()) {
    var range = document.getSelection().getRangeAt(0);
    var container = range.startContainer;
    var offset = range.startOffset;
    range.setStart(target, 0);
    //do your stuff with sel.toString()
    console.log(sel.toString());
    //restore the range
    range.setStart(container, offset);
  }
});

Hope this helps.

Edit: since you said

Note: The user will continue typing as I get the content, so looking for something that doesn't break this flow.

I thought that the click event was just an example; getting the text while user is typing implies:

  1. the entry point can't be click event but probably a setInterval function
  2. while user is typing there is no selections, only the caret

To solve the reported bug is enough changing the code as I did, anyway this is only an example to get the result you are interested in; than you have to work on it to achieve the desired behavior for all the possible case of your real scenario.

Daniele Ricci
  • 15,422
  • 1
  • 27
  • 55
  • Thanks a heap for this, I actually had a feeling something like this was possible I just couldn't get it to work, appreciate the help. I have noticed one bug with this implementation though, if the user highlights some text (makes a selection) and then clicks the highlighted text to unhighlight (the browsers default behavior) it remains highlighted. For my use case I wouldn't mind knowing what the highlighted text was. Having a play at the moment. – Sean Bannister Jun 17 '20 at 13:30
  • @SeanBannister Seems like a browser implementation bug, as the bug doesn't show up on Firefox or EdgeHTML. You could report it to the Chrome dev team. – Jonathan Rosa Jun 17 '20 at 13:44