Deprecated: Assigning the return value of new by reference is deprecated in /homepages/32/d230485870/htdocs/jessmann/blog/wp-settings.php on line 520

Deprecated: Assigning the return value of new by reference is deprecated in /homepages/32/d230485870/htdocs/jessmann/blog/wp-settings.php on line 535

Deprecated: Assigning the return value of new by reference is deprecated in /homepages/32/d230485870/htdocs/jessmann/blog/wp-settings.php on line 542

Deprecated: Assigning the return value of new by reference is deprecated in /homepages/32/d230485870/htdocs/jessmann/blog/wp-settings.php on line 578

Deprecated: Function set_magic_quotes_runtime() is deprecated in /homepages/32/d230485870/htdocs/jessmann/blog/wp-settings.php on line 18

Strict Standards: Declaration of Walker_Page::start_lvl() should be compatible with Walker::start_lvl(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1199

Strict Standards: Declaration of Walker_Page::end_lvl() should be compatible with Walker::end_lvl(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1199

Strict Standards: Declaration of Walker_Page::start_el() should be compatible with Walker::start_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1199

Strict Standards: Declaration of Walker_Page::end_el() should be compatible with Walker::end_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1199

Strict Standards: Declaration of Walker_PageDropdown::start_el() should be compatible with Walker::start_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1244

Strict Standards: Declaration of Walker_Category::start_lvl() should be compatible with Walker::start_lvl(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1391

Strict Standards: Declaration of Walker_Category::end_lvl() should be compatible with Walker::end_lvl(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1391

Strict Standards: Declaration of Walker_Category::start_el() should be compatible with Walker::start_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1391

Strict Standards: Declaration of Walker_Category::end_el() should be compatible with Walker::end_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1391

Strict Standards: Declaration of Walker_CategoryDropdown::start_el() should be compatible with Walker::start_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/classes.php on line 1442

Strict Standards: Redefining already defined constructor for class wpdb in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/wp-db.php on line 306

Strict Standards: Redefining already defined constructor for class WP_Object_Cache in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/cache.php on line 431

Strict Standards: Declaration of Walker_Comment::start_lvl() should be compatible with Walker::start_lvl(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/comment-template.php on line 1266

Strict Standards: Declaration of Walker_Comment::end_lvl() should be compatible with Walker::end_lvl(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/comment-template.php on line 1266

Strict Standards: Declaration of Walker_Comment::start_el() should be compatible with Walker::start_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/comment-template.php on line 1266

Strict Standards: Declaration of Walker_Comment::end_el() should be compatible with Walker::end_el(&$output) in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/comment-template.php on line 1266

Strict Standards: Redefining already defined constructor for class WP_Dependencies in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/class.wp-dependencies.php on line 31

Strict Standards: Redefining already defined constructor for class WP_Http in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/http.php on line 61

Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method GoogleSitemapGenerator::Enable() should not be called statically in /homepages/32/d230485870/htdocs/jessmann/blog/wp-includes/plugin.php on line 339
Jess Mann » Blog Archive » TinyMCE wordcount with jQuery -

TinyMCE wordcount with jQuery

I was recently working on a custom content management system for a client, and I finished everything up well before our deadline. So, as I usually do, I started work on a few small additions to the software just to round off some of the functionality. One area I felt could use some improvement was the wysiwyg editor in the backend. Now, don’t get me wrong… I’ve used TinyMCE in the past, and in comparison to many other popular options such as fkceditor and wymeditor, I’ve been extraordinarily happy with it. However, the user interface looks rather plain, and there are some other simple widgets I felt could be tacked on.

One thing I was interested in adding was a “word count” area right below the editor. Looking around in the plugin repository and on google, I didn’t see any real viable solutions to this. Most involved clicking a button to check the word count, which to me, seemed a bit too clunky. I wanted something simple, where the wordcount was prominantly visible (but unobtrusive) at the bottom of the editor, and was automatically updated *every new word*.

Here’s a basic rundown of what I did (code is to follow):

First, the tricky part: We needed to add an area for the wordcount below the editor automatically. This way, when TinyMCE is instantiated for every textarea, the wordcount will drop in neatly without any extra markup. So, let’s create a function to do just that:

function addWordCount() {
$(’span.mceEditor’).after(’<div id=”‘ + tinyMCE.activeEditor.id + ‘_wordcount” class=”wordcount”>0  words, 0 characters</div>’);
return true;
}

This function simply searches for every tinyMCE instance, and appends a div (with a specific id) after the editor. Now, we’re going to call this function in TinyMCE’s “setup” function, so every time a new TinyMCE instance is created, we drop in a new “wordcount” div right below it.

Next, we need to figure out how to count the words. To do this, we need to get the editor’s contents, replace all the html so it doesn’t muddle our results, trim out any extra space, and then count the number of words (assuming a word is any alphanumeric string between spaces). This looks something like:

var text = ed.getContent().replace(/(<([^>]+)>)/g,”").replace(/\s+/g,” “);
text = $.trim(text);
var words = text.split(’ ‘).length;
var letters = text.length;

Lastly, now that we have the wordcount and have created the wordcount section, we’re going to tack on some code to change the contents of this “wordcount” div on every keystroke. We do this by adding on “onKeyUp” event listener, which retrieves the current editor contents, counts the words, and replaces the wordcount numbers. Our added code looks like this:

ed.onKeyUp.add(function(ed, e) {
$(’#’ + tinyMCE.activeEditor.id + ‘_wordcount’).html(”<span>” + words + “</span> words, <span>” + letters + “</span> characters”);
});

That’s it! Now, just for one last improvement, let’s combine all the code so that the wordcount doesn’t appear until the user has typed at least one letter (just to make things easier on the eyes), then we’re done! Here’s the final code for “setup”:

setup: function(ed) {
var text = “”;
var wordcount = false;
ed.onKeyUp.add(function(ed, e) {
if (!wordcount) {
wordcount = addWordCount();
}
text = ed.getContent().replace(/(<([^>]+)>)/g,”").replace(/\s+/g,” “);
text = $.trim(text);
$(’#’ + tinyMCE.activeEditor.id + ‘_wordcount’).html(”<span>” + text.split(’ ‘).length + “</span> words, <span>” + text.length + “</span> characters”);
});
}

15 Comments

  1. Fred L. says:

    sounds really great, couldn’t figure out how to make it work though. I tried to paste the code at different places, with no success, it just removes all my other plugins from the textarea.

  2. Fred L. says:

    There seems to be a problem with your quotes, but even after correction, I still can’t make it work

  3. admin says:

    Hi Fred,

    Sorry you’re having trouble. Here’s a copy of my TinyMCE init section. Perhaps you can work it out from this.

    tinyMCE.init({
            mode : "specific_textareas",
            editor_selector: "editor",
            theme : "advanced",
            theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,bullist,numlist,|,
    justifyleft,justifycenter,justifyright,justifyfull,|,link,unlink,|,spellchecker,code,
    fullscreen",
            theme_advanced_buttons2:"formatselect,forecolor,|,pastetext,pasteword,removeformat,
    |,charmap,search,replace,|,outdent,indent,|,undo,redo",
            theme_advanced_buttons3:"",
            theme_advanced_buttons4:"",
            language:"en",
            spellchecker_languages:"+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,
    German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv",
            theme_advanced_toolbar_location:"top",
            theme_advanced_toolbar_align:"left",
            theme_advanced_statusbar_location:"bottom",
            theme_advanced_resizing:"1",
            theme_advanced_resize_horizontal:"",
            dialog_type:"modal", relative_urls:"", remove_script_host:"", convert_urls:"",
            remove_linebreaks:"1", paste_convert_middot_lists:"1",
            paste_remove_spans:"1", paste_remove_styles:"1", gecko_spellcheck:"1",
            entities:"38,amp,60,lt,62,gt", accessibility_focus:"1", tab_focus:":prev,:next",
            plugins:"safari,inlinepopups,autosave,spellchecker,paste,fullscreen,searchreplace,
    save,contextmenu,noneditable,visualchars",
            content_css : "http://jess-mann.com/js/tinymce/style.css",
            apply_source_formatting : true,
            setup: function(ed) {
                    var text = "";
                    var wordcount = false;
                    ed.onKeyUp.add(function(ed, e) {
                            if (!wordcount) {
                                    wordcount = addWordCount();
                            }
                            text = ed.getContent().replace(/(< ([^>]+)<)/g,"").
    replace(/\s+/g," ");
                            text = $j.trim(text);
                            $j('#' + tinyMCE.activeEditor.id + '_wordcount').html("<span>" +
    text.split(' ').length + "</span> words, <span>" + text.length + "</span> characters");
                    });
            }
    });
    function addWordCount() {
            $j('span.mceEditor').after('<div id="' + tinyMCE.activeEditor.id + '_wordcount"
    class="wordcount">0 words, 0 characters</div>');
            return true;
    }
    

    Also, I have the css defined as such:

    form div.wordcount {
        float: left;
        clear: both;
        padding: 2px 10px;
        border: 1px solid #ccc;
        border-top: 0px none;
        margin-left: 30px;
        font-size: 110%;
    }
    

    Hope that helps. It works perfectly for me. If you aren’t able to get it working at all, I’d be glad to help out.

  4. Crysta Stemme says:

    A genuinely beneficial publish by you my good friend. We have bookmarked this web page.

  5. แทงบอล says:

    I’ve really enjoyed reading your articles. You obviously know what you are talking about! Your site is so easy to navigate too, I’ve bookmarked it in my favourites.

  6. Rob says:

    Developers, developers, developers! As someone famous once said.

  7. Christian Louboutin says:

    Great writing! You might want to follow up to this topic =D

  8. Andy Br says:

    You save my day, thanks !

  9. Nestor Ser says:

    Your webpage appears to be excellent. Have a very decent morning.

  10. Salvador says:

    Hi Admin, thanks for your code,

    But….I get this error on line
    Syntax error in the HTML file D:\My Sites\TeleNotWebII\ES\PAGE_FNotas.htm line 82: the sequence of ‘ ‘ characters was found while an alphanumeric character string was expected.

    text = ed.getContent().replace(/(]+)<)/g,”").replace(/\s+/g,” “);
    ^
    (006, ERR_PARSER)

    could you help me please…
    Regards

  11. john says:

    hi jess,
    this is working fine.
    this is only working for keyboard event.

    if i want to count char/words when user click cut/paste button of editor’s toolbar then what i have to do ?

    I can call this function but i dnt know where i have to put this.

    Thanks

  12. Peter says:

    Very good, thanks! Although I had to write “$” instead of “$j”.

    I also moved the functions out into a separate js file as so…

    function tmceWordcount(ed) {
    var text = “”;
    var wordcount = false;
    ed.onKeyUp.add(function(ed, e) {
    if (!wordcount) { wordcount = addWordCount(ed); }
    text = ed.getContent().replace(/(]+)<)/g,”").replace(/\s+/g,” “);
    text = $.trim(text);
    $(’#’ + ed.id + ‘_wordcount’).html(”" + text.split(’ ‘).length + ” words, ” + text.length + ” characters”);
    });
    }

    function addWordCount(ed) {
    $(’span.mceEditor’).after(’0 words, 0 characters’);
    return true;
    }

    then add the function call to the “init()” using the line:

    setup:’tmceWordcount’

  13. admin says:

    @John Hi John. To do that, you’d just have to create a new button, and add the functionality to that instead of to the onKeyUp event. I’m not a fan of the idea, but there is actually another wordcount plugin which works this way. If you have any trouble tracking it down, let me know and I might be able to help.

  14. admin says:

    @Peter Thanks for the contribution. I sent you an email about this, actually! Really appreciate the feedback :)

  15. Jonas Holm says:

    Great post,
    This doesn’t work with multiple instances of tinyMCE, thought. To be able to do so I use the following code:

    setup: function (ed) {
    var text = “”;
    var wordcount = false;
    ed.onKeyUp.add(function (ed, e) {
    if (!wordcount) {
    $(’#’ + ed.id +’_parent’).after(’0 words, 0 characters’);
    wordcount = true;
    }
    text = ed.getContent().replace(/(]+)<)/g, “”).replace(/\s+/g, ” “);
    text = $.trim(text);
    $(’#’ + ed.id + ‘_wordcount’).html(”" +
    text.split(’ ‘).length + ” words, ” + text.length + ” characters”);
    });
    }

Leave a Reply