patternjavascriptMinor
Character and word count functionality
Viewed 0 times
characterwordandcountfunctionality
Problem
I have this JS that has a lot of redundant functionality, and it works but looks unseemly.
I would love some assistance with making this more DRY (Don't Repeat Yourself):
I would love some assistance with making this more DRY (Don't Repeat Yourself):
// This provides character & word counter functionality
counter = function() {
var body_value = $('#post_body').val();
var title_value = $('#post_title').val();
if (body_value.length == 0) {
$('#wordCountBody').html(0);
$('#totalCharsBody').html(0);
return;
}
if (title_value.length == 0) {
$('#wordCountTitle').html(0);
return;
}
var regex = /\s+/gi;
var wordCountBody = body_value.trim().replace(regex, ' ').split(' ').length;
var totalCharsBody = body_value.length;
var wordCountTitle = title_value.trim().replace(regex, ' ').split(' ').length;
$('#wordCountBody').html(wordCountBody);
$('#totalCharsBody').html(totalCharsBody);
$('#wordCountTitle').html(wordCountTitle);
};
$(document).ready(function() {
$('#count').click(counter);
$('#post_body').change(counter);
$('#post_body').keydown(counter);
$('#post_body').keypress(counter);
$('#post_body').keyup(counter);
$('#post_body').blur(counter);
$('#post_body').focus(counter);
$('#post_title').change(counter);
$('#post_title').keydown(counter);
$('#post_title').keypress(counter);
$('#post_title').keyup(counter);
$('#post_title').blur(counter);
$('#post_title').focus(counter);
});Solution
Your code looks mostly fine—if you're just performing this specific functionality in one place, some repetition is okay—but there's one thing that could definitely be simplified. Your
If, however, you really wanted to reduce all repetition, you could generalize the
Your counter function currently performs two actions (word counting and character counting) on two separate elements (the body and the title). To make the code as DRY as possible, you should employ the single responsibility principle to break up the function into composable bits.
Your event handler code would then become this:
Is that better? Worse? Well, it's certainly less clear, but it's also much more reusable. If you're going to use this type of functionality many times, it might be worth it, but otherwise, I might prefer the simpler, more explicit version.
ready function could be reduced to simply this:$(document).ready(function () {
$('#count').click(counter);
$('#post_body, #post_title').on('change keydown keypress keyup blur focus', counter);
});If, however, you really wanted to reduce all repetition, you could generalize the
counter function until you ended up with a fairly reusable component.Your counter function currently performs two actions (word counting and character counting) on two separate elements (the body and the title). To make the code as DRY as possible, you should employ the single responsibility principle to break up the function into composable bits.
function countUpdater(element, transformations) {
return function () {
var value = element.val();
transformations.forEach(function (transformation) {
transformation.display.html(value.length === 0 ? 0 : transformation.counter(value));
});
}
}
function wordCounter(value) {
return value.trim().replace(/\s+/g, ' ').split(' ').length;
}
function characterCounter(value) {
return value.length;
}Your event handler code would then become this:
$(document).ready(function () {
var changeEvents = 'change keydown keypress keyup blur focus';
var bodyUpdater = countUpdater($('#post_body'), [
{ display: $('#wordCountBody'), counter: wordCounter },
{ display: $('#totalCharsBody'), counter: characterCounter },
]);
var titleUpdater = countUpdater($('#post_title'), [
{ display: $('#wordCountTitle'), counter: wordCounter },
]);
$('#count').click(function () {
bodyUpdater();
titleUpdater();
});
$('#post_body').on(changeEvents, bodyUpdater);
$('#post_title').on(changeEvents, titleUpdater);
});Is that better? Worse? Well, it's certainly less clear, but it's also much more reusable. If you're going to use this type of functionality many times, it might be worth it, but otherwise, I might prefer the simpler, more explicit version.
Code Snippets
$(document).ready(function () {
$('#count').click(counter);
$('#post_body, #post_title').on('change keydown keypress keyup blur focus', counter);
});function countUpdater(element, transformations) {
return function () {
var value = element.val();
transformations.forEach(function (transformation) {
transformation.display.html(value.length === 0 ? 0 : transformation.counter(value));
});
}
}
function wordCounter(value) {
return value.trim().replace(/\s+/g, ' ').split(' ').length;
}
function characterCounter(value) {
return value.length;
}$(document).ready(function () {
var changeEvents = 'change keydown keypress keyup blur focus';
var bodyUpdater = countUpdater($('#post_body'), [
{ display: $('#wordCountBody'), counter: wordCounter },
{ display: $('#totalCharsBody'), counter: characterCounter },
]);
var titleUpdater = countUpdater($('#post_title'), [
{ display: $('#wordCountTitle'), counter: wordCounter },
]);
$('#count').click(function () {
bodyUpdater();
titleUpdater();
});
$('#post_body').on(changeEvents, bodyUpdater);
$('#post_title').on(changeEvents, titleUpdater);
});Context
StackExchange Code Review Q#63184, answer score: 5
Revisions (0)
No revisions yet.