Pema asks:
Is there an easy way to count the number of words in a textarea using ColdFusion?
The quick and not very helpful answer to this is no. ColdFusion is a server side language. It can't introspect anything in a form field until you actually submit the value. But can we do it both client side and server side? Sure! Here is a simple example.
I began with a very simple form.
<form method="post">
<textarea name="body" id="body"></textarea> <input type="submit"><br/>
<span id="count"></span>
</form>
Notice I've added a span under my textarea. This span will be used to print out the number of words in the textarea. Now to hook up the JavaScript:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(document).ready(function() {
var status = $("#count")
$("#body").keyup(function() {
var cVal = $(this).val()
cVal = $.trim(cVal)
if(cVal.length == 0) return
cVal = cVal.replace(/['.?"]/,"")
var words = cVal.split(/\W/)
status.text("You've written "+words.length+" word(s).")
})
})
</script>
I begin by including the CDN version of jQuery. The main thing I want to do is monitor the textarea, but I know that I'll be using that count span quite a bit so I create a variable for it immediately. This means jQuery doesn't need to find the span every time I want to update it.
I've found to the keyup event for the textarea. Within it - I get the value of the text field and trim it. My code to handle the count is based on a blog entry I wrote two years ago: Counting Word Instances in a String. That entry detailed how you would get a count of unique words in a string. My needs are much simpler. I can break the string on word boundaries, using a regular expression, and then simply count it. The replace is there to handle some of the special cases mentioned in the comments.
Of course - by itself that isn't enough. If you really wanted to limit the word count you would need to add code to bind to the form submit. I'd use caution though as this word count isn't perfect. Web developers typically treat form validation in a very binary fashion - either the data is right or wrong. But this is probably a good example of where we would want to let a field pass even if it has too many words. You can always flag it in an email for further investigation.
Anyway - let's wrap this up now by adding the ColdFusion side:
<cfif structKeyExists(form, "body")>
<cfset wordstr = trim(form.body)>
<cfset wordstr = replaceList(wordstr, "'.""?","")>
<cfset words = reMatch("[[:word:]]+", wordstr)>
<cfoutput>
<p>
You submitted #arraylen(words)# word(s).
</p>
</cfoutput>
</cfif>
This is pretty much the exact same code as the JavaScript version. Obviously all of this could be done many different ways. Altogether now - my complete template:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(document).ready(function() {
var status = $("#count")
$("#body").keyup(function() {
var cVal = $(this).val()
cVal = $.trim(cVal)
if(cVal.length == 0) return
cVal = cVal.replace(/['.?"]/,"")
var words = cVal.split(/\W/)
status.text("You've written "+words.length+" word(s).")
})
})
</script>
</head>
<body>
<form method="post">
<textarea name="body" id="body"></textarea> <input type="submit"><br/>
<span id="count"></span>
</form>
<cfif structKeyExists(form, "body")>
<cfset wordstr = trim(form.body)>
<cfset wordstr = replaceList(wordstr, "'.""?","")>
<cfset words = reMatch("[[:word:]]+", wordstr)>
<cfoutput>
<p>
You submitted #arraylen(words)# word(s).
</p>
</cfoutput>
</cfif>
</body>
</html>
Archived Comments
Ray on the server side can't you just do <cfset str = 'This is a sentence' /> <cfoutput>sentence contains #arraylen(str.split('\s'))# words</cfoutput>?
Back in the good old days, I did this with just ListLen(form.body," "), which got the job done for most normal, properly spaced text. Works out perfectly with ColdFusion's way of not counting empty list entries, so lots of white space doesn't skew the count.
Your way is more thorough and accurate though.
You answered the question which asked for a CF based solution. But wouldn't it make better sense to use javascript?
[textarea onkeyup="wordCounter(this);" onkeydown="wordCounter(this);"][/textarea]
And of course the wordCounter function splits the text with spaces and counts the result.
I use something similar to count (and limit) the characters for things like meta description:
[textarea onkeyup="textCounter(this.form.metadesc,'countermetadesc',500);" onkeydown="textCounter(this.form.metadesc,'countermetadesc',500);" id="metadesc" name="metadesc" wrap="physical" style="margin: 0px; width: 100%;" rows="7"/]
[script type="text/javascript"]
function textCounter(field, countfield, maxlimit) {
if (field.value.length > maxlimit) {
field.value = field.value.substring(0, maxlimit);
document.getElementById(countfield).innerHTML = field.value.length;
alert('You have exceded your character limit'); }
else {
document.getElementById(countfield).innerHTML = field.value.length;
}
}
[/script]
<html>
<head>
<script type="text/javascript">
getWordCount = function(textVal){
var wordArray = textVal.split(/\s+/g);
alert('The word count is ' + wordArray.length);
}
</script>
</head>
<body>
<cfform format="html" name="testForm">
<cftextarea rows="5" cols="60" name="testEdit"/>
<br>
<cfinput type="button" name="btnCount" value="Count Words" onClick="getWordCount(this.form.testEdit.value)">
</cfform>
</body>
</html>
@Sean: Sure. As I said, there are many ways to do this, but I wanted it to be similar to the client side version.
@Freelance: Um... I did do it client side. The first part of the blog entry was showing the client side code.
Ah, my error. I read it all too fast.
I'd tell you not to do that - but I've done that many times.
If you want to avoid writing the javascript, you could do it through ajax by binding a cfdiv to a cfc method that does your counting for you. I work with Oracle where I need to know the bytes in a field instead of the characters. Using a CFC to do the counting seems like the only solution without counting on form processing.
You're probably better off sticking with javascript for a simple char count, but the other option is out there.
If you can't count on form processing, wouldn't that mean you can't count on Ajax? Or do you mean you don't want to rely on the client-side count - if so, that makes sense.