ReedyBear's Blog

Word Counter Plugin for Bearblog

It adds a little counter to your edit post page that tells you how many words are in your post.

For the most up-to-date version of it, see my gitlab

I have submitted it to Herman's Bearblog Plugins.

The Code

There are a couple options you can configure at the top of the script. To style it through CSS, use the selector span.counter-display {} on your theme editor page.

Visit https://bearblog.dev/dashboard/customise/ and paste the following code into your Dashboard footer content:

<script>
/*
 Plugin name: Word Counter
 Description: Displays the number of words in the content of your blog post, updates as you type.
 Author: ReedyBear
 Author URI: https://reedybear.bearblog.dev/bearblog/ 
 Author URI 2: https://gitlab.com/taeluf/other/bearblog-stuff
*/
(function() {
    'use strict';



    ////// CONFIGURATIONS //////
    /* This text is appended to the word count. Does _not_ accept HTML */
    let counter_text = " words ";
    /* the word counter display is appended to this element */
    let counter_parent_selector = "form.post-form > p.sticky-controls";
    /* The class used for styling. Use `span.counter-display` in your CSS (for default) */
    let counter_css_class = "counter-display";



    // create the element here so it can be referenced by the functions.
    let counter_display = document.createElement('span');
    counter_display.classList.add(counter_css_class);
    // We normally only update wordcount after space/enter/backspace. But if the last key was a space/enter/backspace, then we'll allow a wordcount update on the next keypress.
    let pending_update = true;

    let update_word_count = function(event_name, event){

        // we only update wordcount for certain keys
        // we always update word count on click and on change
        if (event_name == 'keyup'){
            if (event.code == 'Space'
                || event.code == 'Enter'
                || event.code == 'Backspace'
            ){
                pending_update = true;
                // do nothing, i.e. DONT return, so that we perform word count
            } else if (pending_update === false){
                // console.log(event.code);
                // its not a whitespace, so we DO RETURN and do NOT update word count
                return;
            } else {
                pending_update = false;
            }
        } 
        // KNOWN ISSUE: With cut/paste event, pending_update doesn't work. The setTimeout causes it to lose scope (I think), and so even if I set pending_update=true, it doesn't actually cause an update on the next keypress.

        const body_content = document.querySelector('textarea#body_content');
        if (body_content == null) return;

        const text = body_content.value;
        // Word count code modified from examples at https://stackoverflow.com/a/18679657
        // Check if text is empty
        // Else `/\s+/`matches a series of whitespace characters
        //    we get array with only words
        //    and check its length
        const num_words = text.trim() == '' ? 0 : text.trim().split(/\s+/).length;

        counter_display.innerText = num_words + counter_text;
    }

    const activate_word_counter = function(){
        const is_edit_post_page = document.querySelector('body.edit-page');
        if (is_edit_post_page == null)return;

        const body_content = document.querySelector('textarea#body_content');
        if (body_content == null) return;

        const sticky_controls = document.querySelector(counter_parent_selector);
        if (sticky_controls == null){
            console.log("Cannot add word counter because '"+counter_parent_selector+"' not found");
            return;
        }

        sticky_controls.appendChild(counter_display);

        body_content.addEventListener('keyup', update_word_count.bind(null, 'keyup'));
        body_content.addEventListener('change', update_word_count.bind(null, 'change'));
        body_content.addEventListener('click', update_word_count.bind(null, 'click'));
        // Cut & Paste events trigger BEFORE the cut or paste is actually performed
        // setTimeout with 0 delay causes it to "execute in the next event cycle", per mdn
        body_content.addEventListener('cut', 
            setTimeout.bind(null,update_word_count.bind(this, 'cut'),0)
        );
        body_content.addEventListener('paste', 
            setTimeout.bind(null,update_word_count.bind(this, 'paste'),0)
        );
        update_word_count('manual');
    };

    if (document.readyState === "loading"){
        document.addEventListener('DOMContentLoaded', activate_word_counter);
    } else {
        activate_word_counter();
    }
})();

</script>

#bearblog #bearblog-featured