How to have a word count limit on your LearnDash essays
I see this request sometimes, and it makes sense.
When it comes to grading Essay questions (sometimes known as “Open Answer” questions), you may not have time to read the equivalent of War and Peace, hundreds of times over.
This is where a word count limitation can come in handy.

In this “tutorial” I’ll basically just provide a massive snippet containing the PHP and JavaScript code necessary to enable a word count limiter for all of your LearnDash essays.
Then, I’ll explain what is going on, function by function.
The Snippet
See our guide on adding PHP code snippets to your WordPress site, if unfamiliar.
function lmscoder_learndash_essay_word_limit() { ?> <script> // Set constants const WORD_LIMIT = 100; const WORD_LIMIT_REACHED_ALERT = 'You have reached the word limit.'; const WORD_COUNT_CLASS = 'lmscoder-word-count'; const WORD_COUNT_ITEM_CLASS = WORD_COUNT_CLASS + '__item'; const WORD_COUNT_LABEL_CLASS = WORD_COUNT_CLASS + '__label'; const WORD_COUNT_VALUE_CLASS = WORD_COUNT_CLASS + '__value'; const WORD_LIMIT_LABEL_CLASS = WORD_COUNT_CLASS + '__label'; const WORD_LIMIT_VALUE_CLASS = WORD_COUNT_CLASS + '__value'; // Check if something is a word or not function isWord( str ) { var alphaNumericFound = false; for ( var i = 0; i < str.length; i++ ) { var code = str.charCodeAt( i ); if ( ( code > 47 && code < 58 ) || // numeric (0-9) ( code > 64 && code < 91 ) || // upper alpha (A-Z) ( code > 96 && code < 123 ) ) { // lower alpha (a-z) alphaNumericFound = true; return alphaNumericFound; } } return alphaNumericFound; } // Insert some HTML so we don't have to do template overrides function elementBuilder( element ) { const markup = ` <div class="${ WORD_COUNT_CLASS }"> <div class="${ WORD_COUNT_ITEM_CLASS }"> <span class="${ WORD_COUNT_LABEL_CLASS }">Word count: </span> <span class="${ WORD_COUNT_VALUE_CLASS }">0</span> </div> <div class="${ WORD_COUNT_ITEM_CLASS }"> <span class="${ WORD_LIMIT_LABEL_CLASS }">Word limit: </span> <span class="${ WORD_LIMIT_VALUE_CLASS }">${ WORD_LIMIT }</span> </div> </div> `; element.parentElement.insertAdjacentHTML( 'beforeend', markup ); } // Set the word count function setWordCount( element ) { var text = element.value.split( ' ' ); var wordCount = 0; for ( var i = 0; i < text.length; i++ ) { if ( ! text[i] == ' ' && isWord( text[i] ) ) { wordCount++; } } element.parentElement.querySelector( '.' + WORD_COUNT_VALUE_CLASS ).textContent = wordCount; } // Get the word count function getWordCount( element ) { return element.parentElement.querySelector( '.' + WORD_COUNT_VALUE_CLASS ).textContent; } // Wordcountify any textarea element passed to it function wordCountify( element ) { elementBuilder( element ); element.addEventListener( 'keydown', function( e ) { if ( getWordCount( element ) >= WORD_LIMIT && e.code !== 'Backspace' ) { e.preventDefault(); return alert( WORD_LIMIT_REACHED_ALERT ); } } ); element.addEventListener( 'keyup', function( e ) { setWordCount( element ); } ); } // Select all LearnDash essay textareas const essayTextareas = document.querySelectorAll( '.wpProQuiz_questionEssay' ); // Wordcountify the selected essay textareas essayTextareas.forEach( element => wordCountify( element ) ); </script> <?php } add_action( 'wp_footer', 'lmscoder_learndash_essay_word_limit' );
After implementing this, you should see a functional word counter beneath each of your LearnDash essay questions. You’ll also see the word limit, and run into an error should you reach the word limit.
The Explanation
Don’t want to just dump a code snippet on you without explaining it at all!
We’ve broken up the snippet into a number of comments.
Part #1: Set constants
Here is a nice and convenient place to change constants for your needs.
I used the “lmscoder-” CSS prefix just to make sure I didn’t conflict with another plugin that happened to use a generic class name like word-count
.
Although feel free to rename it to any non-conflicting-on-your-site name you’d like, if you want to hide that you got this snippet from LMSCoder, or for any other reason. I don’t care!
This is also the place where you set the sitewide essay word count limit. The scope of this tutorial does not include setting the word count limit of individual essays.
Finally, this is the place where you can customize the message alert that appears when the student reaches the word limit.
Part #2: Insert some HTML so we don’t have to do template overrides
This is where we create and insert the HTML underneath each essay textarea.
For a fun bonus, I originally wrote this function as follows:
function elementBuilderOld( element ) { wordCountWrapperEl = document.createElement( 'p' ); wordCountFirstChild = document.createElement( 'span' ); wordCountSecondChild = document.createElement( 'span' ); wordCountWrapperEl.classList.add( 'lmscoder-word-count' ); wordCountFirstChild.classList.add( 'lmscoder-word-count__label' ); wordCountSecondChild.classList.add( 'lmscoder-word-count__value' ); wordCountFirstChild.textContent = 'Word count: '; wordCountSecondChild.textContent = '0'; wordCountWrapperEl.prepend( wordCountFirstChild ); wordCountWrapperEl.append( wordCountSecondChild ); element.parentElement.append( wordCountWrapperEl ); }
Seemed a bit verbose just so I could make a couple lines of HTML. Then I remembered template literals!
Part #3: Set the word count
This function gets the textarea element passed to it, scans the value, and counts the number of words in it.
Then, it updates the value in the WORD_COUNT_VALUE_CLASS element.
Part #4: Get the word count
This function retrieves the value in the WORD_COUNT_VALUE_CLASS. It’s leveraged when checking to see if the student has reached the word limit.
Part #5: Wordcountify any textarea element passed to it
This function can be used to “wordcountify” every element passed to it.
It adds an event listener to the “keyup” event which checks to see how many words are in the textarea, and updates the count accordingly.
It adds an event listener to the “keyup” event, which checks to see if the word limit has been reached. If it has, an alert will pop up. Only the backspace key is allowed to be typed until the word count is reduced.
Otherwise they may find the word limit enforcement experience frustrating, as any other key pressed will trigger to an alert popup.
This is nice, because it means this will work even when a quiz contains multiple essay questions.
It can also be used for any textarea element on your WordPress site, such as comment forms.
Because of this flexibility, 99% of this snippet can be filed under not-LearnDash-specific.
Part #6: Select all LearnDash essay textareas
This is pretty much the only part of the snippet that specifically relates to LearnDash.
It selects all the LearnDash essay textarea elements on the page, which are selectable with the .wpProQuiz_questionEssay
selector.
But I just want a word count displayed, no limit!
You’re in luck because all you have to do is take the exact same snippet from above, and delete some stuff.
Step #1: Remove constants related to word limits
That means delete the following lines:
const WORD_LIMIT = 100; const WORD_LIMIT_REACHED_ALERT = 'You have reached the word limit.'; const WORD_LIMIT_LABEL_CLASS = WORD_COUNT_CLASS + '__label'; const WORD_LIMIT_VALUE_CLASS = WORD_COUNT_CLASS + '__value';
I guess keeping them won’t hurt although seems like a waste of space if you don’t want a word limit.
Step #2: Remove HTML related to word limits
That means, get rid of this part:
<div class="${ WORD_COUNT_ITEM_CLASS }"> <span class="${ WORD_LIMIT_LABEL_CLASS }">Word limit: </span> <span class="${ WORD_LIMIT_VALUE_CLASS }">${ WORD_LIMIT }</span> </div>
Probably don’t want to display anything related to “Word Limits” if you don’t have any. Also that WORD_LIMIT constant doesn’t exist anymore because we deleted it in the previous step.
Step #3: Remove keydown event listener
This is what we were using to check if the word limit had been reached.
If the student pressed a key down (get it? keydown
events happen when a key is pressed down?) then there would be a check to see
That means, remove this part:
element.addEventListener( 'keydown', function( e ) { if ( getWordCount( element ) >= WORD_LIMIT && e.code !== 'Backspace' ) { e.preventDefault(); return alert( WORD_LIMIT_REACHED_ALERT ); } } );
Also since we’re no longer using getWordCount
function anywhere else in our code at this point, you can delete that as well.
This time I’m not going to tell you exactly what lines to delete. Try to find and delete the getWordCount
function on your own as practice!
Credits
A big thanks to Kelvin Gobo whose word-counter-js code I refactored and adapted to make work for multiple textarea elements on the same page (as may be the case with a LearnDash quizzes with multiple essay questions).
For further explanation on how the isWord function determines what a “word” is, see Kelvin’s article on CodeSource titled Building a Word Counter in JavaScript.
Taking it further
I can think of a few ways to iterate take this concept further.
1) Another way of alerting, that doesn’t involve a JavaScript alert, although I guess that’s tough to miss.
2) Setting a word count minimum, instead of a maximum, or in conjunction with a maximum.
3) Setting a word count minimum/maximum per essay question, instead of a hardcoded sitewide setting.
4) Setting exclusion rules, like if one particular student really wants to exceed a word limit, then that can be allowed on a per-user and/or per-question basis.
5) A character limit, instead of a word limit. Maybe you’re running a course about how to write effective tweets?
6) Enhanced “security” because imposing word limits solely through client-side code means that a technically-savvy student could get around them. Perhaps there could be server-side code to reject essays beyond the word limit as well? Seems a bit overkill, to be honest.
The complexities can really compound depending on expectations, so if you need something beyond what this tutorial covers, feel free to contact me.