TinyMCE, show character count instead of word count

I was able to set the wordcount plugin to display characters by default by creating a custom version of the silver theme. Looks like in TinyMCE 5.1.6 the way plugins are rendered is set in the theme file. TinyMCE config:

{
    selector: '.tinymce',
    theme: 'silver-custom',
    ...
}

The theme file is a copy of the themes/silver/theme.js and needs to load after TinyMCE.

Changes:

 ...
 function Theme () {
      global$1.add('silver-custom', function (editor) {
 ...

and

...
var renderWordCount = function (editor, providersBackstage) {
    ...
    store: {
        mode: 'memory',
        initialValue: {
        mode: 'characters',
        ...
}
...

    init_instance_callback: function (editor) {
editor.on('change', function (e) {
                var length = editor.contentDocument.body.innerText.length;
            });
}

On init add this. length is your character length. Now you need to hide word count and attach a new string with character counter.


wordcount plugin now can count and show characters:

Clicking Word Count in the status bar switches between counting words and characters.

By default mode is "words", but it's pretty easy emulate click in status bar to switch it.

Alter your editor config following way:

tinymce.init({
   plugins: "wordcount",

   // ... 

   init_instance_callback: function (editor) {
      $(editor.getContainer()).find('button.tox-statusbar__wordcount').click();  // if you use jQuery
   }
});

That's all. You have character count now.


Write your own plugin.

The following solution is based on this article. The charactercount plugin counts the actual characters that the user sees, all HTML and hidden characters are ignored. The number is updated on every "key up" event.

Character Count Plugin:

tinymce.PluginManager.add('charactercount', function (editor) {
  var self = this;

  function update() {
    editor.theme.panel.find('#charactercount').text(['Characters: {0}', self.getCount()]);
  }

  editor.on('init', function () {
    var statusbar = editor.theme.panel && editor.theme.panel.find('#statusbar')[0];

    if (statusbar) {
      window.setTimeout(function () {
        statusbar.insert({
          type: 'label',
          name: 'charactercount',
          text: ['Characters: {0}', self.getCount()],
          classes: 'charactercount',
          disabled: editor.settings.readonly
        }, 0);

        editor.on('setcontent beforeaddundo', update);

        editor.on('keyup', function (e) {
            update();
        });
      }, 0);
    }
  });

  self.getCount = function () {
    var tx = editor.getContent({ format: 'raw' });
    var decoded = decodeHtml(tx);
    // here we strip all HTML tags
    var decodedStripped = decoded.replace(/(<([^>]+)>)/ig, "").trim();
    var tc = decodedStripped.length;
    return tc;
  };

  function decodeHtml(html) {
    var txt = document.createElement("textarea");
    txt.innerHTML = html;
    return txt.value;
  }
});

CSS Tweaks:

/* Optional: Adjust the positioning of the character count text. */
label.mce-charactercount {
  margin: 2px 0 2px 2px;
  padding: 8px;
}

/* Optional: Remove the html path code from the status bar. */
.mce-path {
  display: none !important;
}

TinyMCE Initialization (using jQuery)

$('textarea.tinymce').tinymce({
  plugins: "charactercount",
  statusbar: true,
  init_instance_callback: function (editor) {
    $('.mce-tinymce').show('fast');
    $(editor.getContainer()).find(".mce-path").css("display", "none");
  }
  // ...
});

ps. Use JS minifier.