1
votes

Okay. I'm using TinyMCE and MarkJS to highlight long words in the text. If words are longer than 6 letters they are marked.

I've put together a demo here: http://codepen.io/MarkBuskbjerg/pen/RomqKO

The problem:

At first glance everything is mighty fine. The long words are highligthed perfectly.

But...

If you place the caret inside the word. The highlight disappears on keydown.

Example: Place the caret at the end of the text and arrow key your way throught the text the highlight is gone when the caret is on a highlighted word.

I suspect that the problem is due to TinyMCE triggering some sort of event when the caret is on a word.

Possible solution I've tried to see if the caret is somehow splitting the word and thereby making it seem shorter. Which doesn't seem to be the case (see the console.log(words) on line 24.

** EDIT 30-12-2016:** Seems like it ought to be TinyMCE triggering the error when the caret is inside of a word. Maybe because it execute some other event?

My question I somehow need to find a way to keep the word highlighted while the caret is active on the highlighted span.

Any ideas on how to achieve this is very much appreciated :)

The HTML markup is:

<div class="container">
    <div id="myTextArea" contenteditable="true">
        Lorem ipsum dolores sit amet.
    </div>
</div>

The JavaScript goes like this:

tinymce.init({
  selector: '#myTextArea',
  height: 200,
  setup: function(ed) {
    ed.on('keyup', myCustomInitInstance);
    ed.on('paste', myCustomInitInstance);
    ed.on('cut', myCustomInitInstance);
  },
  init_instance_callback: "myCustomInitInstance",
});

function myCustomInitInstance(inst) {
  var rawText = tinyMCE.get('myTextArea').getContent({
    format: 'text'
  });
  rawText = rawText.replace(/<([^>]+)>|[\.\+\?,\(\)"'\-\!\;\:]/ig, "");

  var words = rawText.split(" ");

  var matchWarning = [];
  var longWord = 6;
  var wordCounter;
  var output;
  for (var i = 0, len = words.length; i < len; i++) {
    if (words[i].length > longWord) {
      matchWarning.push(words[i]);
    }
  }

  var editor = tinyMCE.activeEditor;
   // Store the selection
  var bookmark = editor.selection.getBookmark();
  console.log(bookmark);
   // Remove previous marks and add new ones
   var $ctx = $(editor.getBody());
   $ctx.unmark({
     done: function() {
      $ctx.mark(matchWarning, {
        acrossElements: true,
        //debug: true,
        separateWordSearch: false
      });
    }
  });
  console.log(bookmark);
  // Restore the selection
  editor.selection.moveToBookmark(bookmark);

}
1
First of, you should use the done callback of mark.js, as actions within an iframe are async (see this pen). But this will not solve your problem. Did you make sure it hasn't something to do with your bookmark restore?dude
Thanks for the heads up on the done callback function. I've tried to "debug" with some console logging of the bookmark. But I don't know how the bookmark works well enough. I've tried to find docs on it. But mostly find how to set bookmarks in the browser :) This was the best I've found archive.tinymce.com/wiki.php/… If I log the bookmark (line 33) the ID +1's on every keydown Dunno know if that is to be expected.Mark Buskbjerg
I tried several methods but failed to get two things working at the same time: (1) having the current word highlighted, (2) keeping the cursor/selection after highlighting. I can get one or the other but not both. The fact that mark.js surrounds each word with a mark tag makes the saved bookmark/range invalid when we try to restore it.ConnorsFan

1 Answers

1
votes

Your problem is that you're listening to the keyup event, which is also fired if you're navigating with the arrow keys. And in the callback function of this event listener you've removing the highlighted terms using .unmark(), which is why the highlighting disappears.

To solve this, you need to ignore events from arrow keys. I've done this for you. Also I've refactored your example to simplify the situation, removed unnecessary variables and used TinyMCE v4 methods (which you're using).

Example

tinymce.init({
  selector: "#myTextArea",
  height: 200,
  setup: function(editor) {
    editor.on("init keyup", function(e) {
      var code = (e.keyCode || e.which || 0);
      // do nothing if it's an arrow key
      if (code == 37 || code == 38 || code == 39 || code == 40) {
        return;
      }
      myCustomInitInstance();
    });
  }
});

function myCustomInitInstance() {
  var editor = tinymce.get("myTextArea"),
    rawText = editor.getContent({
      format: "text"
    }).replace(/<([^>]+)>|[\.\+\?,\(\)"'\-\!\;\:]/ig, ""),
    words = rawText.split(" "),
    matchWarning = [],
    longWord = 6;

  for (var i = 0, len = words.length; i < len; i++) {
    if (words[i].length > longWord) {
      matchWarning.push(words[i]);
    }
  }

  console.log(matchWarning);
  var bookmark = editor.selection.getBookmark();
  var $ctx = $(editor.getBody());
  $ctx.unmark({
    done: function() {
      $ctx.mark(matchWarning, {
        acrossElements: true,
        separateWordSearch: false,
        done: function() {
          editor.selection.moveToBookmark(bookmark);
        }
      });
    }
  });

}