Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextRange: Adding options parameter to saveCharacterRanges #366

Open
johnnyji opened this issue Dec 14, 2015 · 1 comment
Open

TextRange: Adding options parameter to saveCharacterRanges #366

johnnyji opened this issue Dec 14, 2015 · 1 comment

Comments

@johnnyji
Copy link

Here's a hypothetical. Given an array of keywords ['apples', 'oranges', 'bananas'], I have a contenteditable div that should highlight any instances of those words as the user types.

Here's what I'm doing right now:

  • When the user types something, I go through the array of keywords and see if there are any keywords in the text that aren't wrapped in a mark tag. I will do a JS replace and wrap all of those words in the mark tags so they're highlighted
  • I also make sure to add a zero width space, so effectively, all instances of apple now become <mark>apple</mark>/u200B.
  • I then set the innerHTML of the contenteditable element to the newly parsed text.

I'm currently using the saveCharacterRanges and restoreCharacterRanges so my caret doesn't go haywire from me dynamically adding HTML. However, the issue is that whenever I dynamically inject the mark tags, the highlighted style drags on as I type and doesn't cut off at the end of the keyword.

I think (not entirely sure) that saveCharacterRanges and restoreCharacterRanges methods recognizes the zero width space, therefore, placing my caret inside the mark tag causing the style to drag on.

I was able to do a hackaround:

  sel.restoreCharacterRanges(findDOMNode(this), savedSelection);
  sel.move('character', 1, {
    characterOptions: {
      ignoreCharacters: '\u200B'
    }
  });
  sel.move('character', -1, {
    characterOptions: {
      ignoreCharacters: '\u200B'
    }
  });

This solves my problem. But now this presents a new problem, where if my caret is ever at the very last position, it will unfocus my contenteditable on change because I'm shifting the character +1 on already the last character.

Is it possible to add options as a parameter to saveCharacterRanges as well so I can safely add HTML tags to contenteditable elements, preserve my caret position and as well as not having the style drag on (by adding a zero width space).

Or maybe I'm interperating this completely wrong, in which case I would love to get some advice on alternate solutions!

Here's the original SO question: http://stackoverflow.com/questions/34262219/setting-caret-after-zero-width-space-using-rangy-textranges-module

Thanks so much for reading through!

@njordhov
Copy link

njordhov commented Jun 5, 2020

whenever I dynamically inject the mark tags, the highlighted style drags on as I type and doesn't cut off at the end of the keyword.

Fast forward five years... The underlying issue is about controlling the text input position within elements. With nested elements, different text input positions may show as the same caret position.

Below, if the caret is after the last letter in apple, typed input will get styled as marked text. But what if it is preferable that any typed input is inserted as if after the </mark> endtag, having the styling of the editable div?

<div contenteditable=true>An <mark>apple</mark> a day...</div>

I have experimented with alternative solutions using pure CSS to no avail, including constraining the width of the mark element.

Using HTML only, it somewhat works to set contenteditable=false on the mark element. At least it forces the input point to stay outside the marked text. Setting a maxlength property won't restrict the marked text - it only takes effect for input and textarea element.

Possibly setEndAfter could be used to set the end of the range to after the element instead of after its last containing character.

What actually worked to expel the cursor from a marked element while keeping the caret in the same place was:

  1. Create an element with a single character text object
  2. Insert it after (or before) the marked element
  3. Move the cursor to its text
  4. Set contenteditable of the marked element to false
  5. Remove the text node.

This forces the insertion cursor to the empty element. Working at least in Chrome and Firefox.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants