[javascript] Counting words in string

I was trying to count words in a text in this way:

function WordCount(str) {
  var totalSoFar = 0;
  for (var i = 0; i < WordCount.length; i++)
    if (str(i) === " ") { // if a space is found in str
      totalSoFar = +1; // add 1 to total so far
  }
  totalsoFar += 1; // add 1 to totalsoFar to account for extra space since 1 space = 2 words
}

console.log(WordCount("Random String"));

I think I have got this down pretty well, except I think that the if statement is wrong. The part that checks if str(i) contains a space and adds 1.

Edit:

I found out (thanks to Blender) that I can do this with a lot less code:

function WordCount(str) { 
  return str.split(" ").length;
}

console.log(WordCount("hello world"));

This question is related to javascript

The answer is


I think this method is more than you want

var getWordCount = function(v){
    var matches = v.match(/\S+/g) ;
    return matches?matches.length:0;
}

Try these before reinventing the wheels

from Count number of words in string using JavaScript

function countWords(str) {
  return str.trim().split(/\s+/).length;
}

from http://www.mediacollege.com/internet/javascript/text/count-words.html

function countWords(s){
    s = s.replace(/(^\s*)|(\s*$)/gi,"");//exclude  start and end white-space
    s = s.replace(/[ ]{2,}/gi," ");//2 or more space to 1
    s = s.replace(/\n /,"\n"); // exclude newline with a start spacing
    return s.split(' ').filter(function(str){return str!="";}).length;
    //return s.split(' ').filter(String).length; - this can also be used
}

from Use JavaScript to count words in a string, WITHOUT using a regex - this will be the best approach

function WordCount(str) {
     return str.split(' ')
            .filter(function(n) { return n != '' })
            .length;
}

Notes From Author:

You can adapt this script to count words in whichever way you like. The important part is s.split(' ').length — this counts the spaces. The script attempts to remove all extra spaces (double spaces etc) before counting. If the text contains two words without a space between them, it will count them as one word, e.g. "First sentence .Start of next sentence".


You got some mistakes in your code.

function WordCount(str) {
    var totalSoFar = 0;
    for (var i = 0; i < str.length; i++) {
        if (str[i] === " ") {
            totalSoFar += 1;
        }
    }
    return totalSoFar + 1; // you need to return something.
}
console.log(WordCount("Random String"));

There is another easy way using regular expressions:

(text.split(/\b/).length - 1) / 2

The exact value can differ about 1 word, but it also counts word borders without space, for example "word-word.word". And it doesn't count words that don't contain letters or numbers.


I'm not sure if this has been said previously, or if it's what is needed here, but couldn't you make the string an array and then find the length?

let randomString = "Random String";

let stringWords = randomString.split(' ');
console.log(stringWords.length);

If you want to count specific words

function countWholeWords(text, keyword) {
    const times = text.match(new RegExp(`\\b${keyword}\\b`, 'gi'));

    if (times) {
        console.log(`${keyword} occurs ${times.length} times`);
    } else {
        console.log(keyword + " does not occurs")
    }
}


const text = `
In a professional context it often happens that private or corporate clients corder a publication to be 
made and presented with the actual content still not being ready. Think of a news blog that's 
filled with content hourly on the day of going live. However, reviewers tend to be distracted 
by comprehensible content, say, a random text copied from a newspaper or the internet.
`

const wordsYouAreLookingFor = ["random", "cat", "content", "reviewers", "dog", "with"]

wordsYouAreLookingFor.forEach((keyword) => countWholeWords(text, keyword));


// random occurs 1 times
// cat does not occurs
// content occurs 3 times
// reviewers occurs 1 times
// dog does not occurs
// with occurs 2 times

Here's my approach, which simply splits a string by spaces, then for loops the array and increases the count if the array[i] matches a given regex pattern.

    function wordCount(str) {
        var stringArray = str.split(' ');
        var count = 0;
        for (var i = 0; i < stringArray.length; i++) {
            var word = stringArray[i];
            if (/[A-Za-z]/.test(word)) {
                count++
            }
        }
        return count
    }

Invoked like so:

var str = "testing strings here's a string --..  ? // ... random characters ,,, end of string";
wordCount(str)

(added extra characters & spaces to show accuracy of function)

The str above returns 10, which is correct!


function totalWordCount() {
  var str ="My life is happy"
  var totalSoFar = 0;

  for (var i = 0; i < str.length; i++)
    if (str[i] === " ") { 
     totalSoFar = totalSoFar+1;
  }
  totalSoFar = totalSoFar+ 1; 
  return totalSoFar
}

console.log(totalWordCount());

Accuracy is also important.

What option 3 does is basically replace all the but any whitespaces with a +1 and then evaluates this to count up the 1's giving you the word count.

It's the most accurate and fastest method of the four that I've done here.

Please note it is slower than return str.split(" ").length; but it's accurate when compared to Microsoft Word.

See file ops/s and returned word count below.

Here's a link to run this bench test. https://jsbench.me/ztk2t3q3w5/1

_x000D_
_x000D_
// This is the fastest at 111,037 ops/s ±2.86% fastest_x000D_
var str = "All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";_x000D_
function WordCount(str) {_x000D_
  return str.split(" ").length;_x000D_
}_x000D_
console.log(WordCount(str));_x000D_
// Returns 241 words. Not the same as Microsoft Word count, of by one._x000D_
_x000D_
// This is the 2nd fastest at 46,835 ops/s ±1.76% 57.82% slower_x000D_
var str = "All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";_x000D_
function WordCount(str) {_x000D_
  return str.split(/(?!\W)\S+/).length;_x000D_
}_x000D_
console.log(WordCount(str));_x000D_
// Returns 241 words. Not the same as Microsoft Word count, of by one._x000D_
_x000D_
// This is the 3rd fastest at 37,121 ops/s ±1.18% 66.57% slower_x000D_
var str = "All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";_x000D_
function countWords(str) {_x000D_
  var str = str.replace(/\S+/g,"\+1");_x000D_
  return eval(str);_x000D_
}_x000D_
console.log(countWords(str));_x000D_
// Returns 240 words. Same as Microsoft Word count._x000D_
_x000D_
// This is the slowest at 89 ops/s 17,270 ops/s ±2.29% 84.45% slower_x000D_
var str = "All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";_x000D_
function countWords(str) {_x000D_
  var str = str.replace(/(?!\W)\S+/g,"1").replace(/\s*/g,"");_x000D_
  return str.lastIndexOf("");_x000D_
}_x000D_
console.log(countWords(str));_x000D_
// Returns 240 words. Same as Microsoft Word count.
_x000D_
_x000D_
_x000D_


function countWords(str) {
    var regEx = /([^\u0000-\u007F]|\w)+/g;  
    return str.match(regEx).length;
}

Explanation:

/([^\u0000-\u007F]|\w) matches word characters - which is great -> regex does the heavy lifting for us. (This pattern is based on the following SO answer: https://stackoverflow.com/a/35743562/1806956 by @Landeeyo)

+ matches the whole string of the previously specified word characters - so we basically group word characters.

/g means it keeps looking till the end.

str.match(regEx) returns an array of the found words - so we count its length.


I know its late but this regex should solve your problem. This will match and return the number of words in your string. Rather then the one you marked as a solution, which would count space-space-word as 2 words even though its really just 1 word.

function countWords(str) {
    var matches = str.match(/\S+/g);
    return matches ? matches.length : 0;
}

function WordCount(str) {
    var totalSoFar = 0;
    for (var i = 1; i < str.length; i++) {
        if (str[i] === " ") {
            totalSoFar ++;
        }
    }
    return totalSoFar; 
}
console.log(WordCount("hi my name is raj));

<textarea name="myMessage" onkeyup="wordcount(this.value)"></textarea>
<script type="text/javascript">
var cnt;
function wordcount(count) {
var words = count.split(/\s/);
cnt = words.length;
var ele = document.getElementById('w_count');
ele.value = cnt;
}
document.write("<input type=text id=w_count size=4 readonly>");
</script>

let leng = yourString.split(' ').filter(a => a.trim().length > 0).length

For those who want to use Lodash can use the _.words function:

_x000D_
_x000D_
var str = "Random String";_x000D_
var wordCount = _.size(_.words(str));_x000D_
console.log(wordCount);
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
_x000D_
_x000D_
_x000D_


After cleaning the string, you can match non-whitespace characters or word-boundaries.

Here are two simple regular expressions to capture words in a string:

  • Sequence of non-white-space characters: /\S+/g
  • Valid characters between word boundaries: /\b[a-z\d]+\b/g

The example below shows how to retrieve the word count from a string, by using these capturing patterns.

_x000D_
_x000D_
/*Redirect console output to HTML.*/document.body.innerHTML='';console.log=function(s){document.body.innerHTML+=s+'\n';};_x000D_
/*String format.*/String.format||(String.format=function(f){return function(a){return f.replace(/{(\d+)}/g,function(m,n){return"undefined"!=typeof a[n]?a[n]:m})}([].slice.call(arguments,1))});_x000D_
_x000D_
// ^ IGNORE CODE ABOVE ^_x000D_
//   =================_x000D_
_x000D_
// Clean and match sub-strings in a string._x000D_
function extractSubstr(str, regexp) {_x000D_
    return str.replace(/[^\w\s]|_/g, '')_x000D_
        .replace(/\s+/g, ' ')_x000D_
        .toLowerCase().match(regexp) || [];_x000D_
}_x000D_
_x000D_
// Find words by searching for sequences of non-whitespace characters._x000D_
function getWordsByNonWhiteSpace(str) {_x000D_
    return extractSubstr(str, /\S+/g);_x000D_
}_x000D_
_x000D_
// Find words by searching for valid characters between word-boundaries._x000D_
function getWordsByWordBoundaries(str) {_x000D_
    return extractSubstr(str, /\b[a-z\d]+\b/g);_x000D_
}_x000D_
_x000D_
// Example of usage._x000D_
var edisonQuote = "I have not failed. I've just found 10,000 ways that won't work.";_x000D_
var words1 = getWordsByNonWhiteSpace(edisonQuote);_x000D_
var words2 = getWordsByWordBoundaries(edisonQuote);_x000D_
_x000D_
console.log(String.format('"{0}" - Thomas Edison\n\nWord count via:\n', edisonQuote));_x000D_
console.log(String.format(' - non-white-space: ({0}) [{1}]', words1.length, words1.join(', ')));_x000D_
console.log(String.format(' - word-boundaries: ({0}) [{1}]', words2.length, words2.join(', ')));
_x000D_
body { font-family: monospace; white-space: pre; font-size: 11px; }
_x000D_
_x000D_
_x000D_


Finding Unique Words

You could also create a mapping of words to get unique counts.

_x000D_
_x000D_
function cleanString(str) {_x000D_
    return str.replace(/[^\w\s]|_/g, '')_x000D_
        .replace(/\s+/g, ' ')_x000D_
        .toLowerCase();_x000D_
}_x000D_
_x000D_
function extractSubstr(str, regexp) {_x000D_
    return cleanString(str).match(regexp) || [];_x000D_
}_x000D_
_x000D_
function getWordsByNonWhiteSpace(str) {_x000D_
    return extractSubstr(str, /\S+/g);_x000D_
}_x000D_
_x000D_
function getWordsByWordBoundaries(str) {_x000D_
    return extractSubstr(str, /\b[a-z\d]+\b/g);_x000D_
}_x000D_
_x000D_
function wordMap(str) {_x000D_
    return getWordsByWordBoundaries(str).reduce(function(map, word) {_x000D_
        map[word] = (map[word] || 0) + 1;_x000D_
        return map;_x000D_
    }, {});_x000D_
}_x000D_
_x000D_
function mapToTuples(map) {_x000D_
    return Object.keys(map).map(function(key) {_x000D_
        return [ key, map[key] ];_x000D_
    });_x000D_
}_x000D_
_x000D_
function mapToSortedTuples(map, sortFn, sortOrder) {_x000D_
    return mapToTuples(map).sort(function(a, b) {_x000D_
        return sortFn.call(undefined, a, b, sortOrder);_x000D_
    });_x000D_
}_x000D_
_x000D_
function countWords(str) {_x000D_
    return getWordsByWordBoundaries(str).length;_x000D_
}_x000D_
_x000D_
function wordFrequency(str) {_x000D_
    return mapToSortedTuples(wordMap(str), function(a, b, order) {_x000D_
        if (b[1] > a[1]) {_x000D_
            return order[1] * -1;_x000D_
        } else if (a[1] > b[1]) {_x000D_
            return order[1] * 1;_x000D_
        } else {_x000D_
            return order[0] * (a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0));_x000D_
        }_x000D_
    }, [1, -1]);_x000D_
}_x000D_
_x000D_
function printTuples(tuples) {_x000D_
    return tuples.map(function(tuple) {_x000D_
        return padStr(tuple[0], ' ', 12, 1) + ' -> ' + tuple[1];_x000D_
    }).join('\n');_x000D_
}_x000D_
_x000D_
function padStr(str, ch, width, dir) { _x000D_
    return (width <= str.length ? str : padStr(dir < 0 ? ch + str : str + ch, ch, width, dir)).substr(0, width);_x000D_
}_x000D_
_x000D_
function toTable(data, headers) {_x000D_
    return $('<table>').append($('<thead>').append($('<tr>').append(headers.map(function(header) {_x000D_
        return $('<th>').html(header);_x000D_
    })))).append($('<tbody>').append(data.map(function(row) {_x000D_
        return $('<tr>').append(row.map(function(cell) {_x000D_
            return $('<td>').html(cell);_x000D_
        }));_x000D_
    })));_x000D_
}_x000D_
_x000D_
function addRowsBefore(table, data) {_x000D_
    table.find('tbody').prepend(data.map(function(row) {_x000D_
        return $('<tr>').append(row.map(function(cell) {_x000D_
            return $('<td>').html(cell);_x000D_
        }));_x000D_
    }));_x000D_
    return table;_x000D_
}_x000D_
_x000D_
$(function() {_x000D_
    $('#countWordsBtn').on('click', function(e) {_x000D_
        var str = $('#wordsTxtAra').val();_x000D_
        var wordFreq = wordFrequency(str);_x000D_
        var wordCount = countWords(str);_x000D_
        var uniqueWords = wordFreq.length;_x000D_
        var summaryData = [_x000D_
            [ 'TOTAL', wordCount ],_x000D_
            [ 'UNIQUE', uniqueWords ]_x000D_
        ];_x000D_
        var table = toTable(wordFreq, ['Word', 'Frequency']);_x000D_
        addRowsBefore(table, summaryData);_x000D_
        $('#wordFreq').html(table);_x000D_
    });_x000D_
});
_x000D_
table {_x000D_
    border-collapse: collapse;_x000D_
    table-layout: fixed;_x000D_
    width: 200px;_x000D_
    font-family: monospace;_x000D_
}_x000D_
thead {_x000D_
    border-bottom: #000 3px double;;_x000D_
}_x000D_
table, td, th {_x000D_
    border: #000 1px solid;_x000D_
}_x000D_
td, th {_x000D_
    padding: 2px;_x000D_
    width: 100px;_x000D_
    overflow: hidden;_x000D_
}_x000D_
_x000D_
textarea, input[type="button"], table {_x000D_
    margin: 4px;_x000D_
    padding: 2px;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>_x000D_
_x000D_
<h1>Word Frequency</h1>_x000D_
<textarea id="wordsTxtAra" cols="60" rows="8">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal._x000D_
_x000D_
Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this._x000D_
_x000D_
But, in a larger sense, we can not dedicate -- we can not consecrate -- we can not hallow -- this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us -- that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion -- that we here highly resolve that these dead shall not have died in vain -- that this nation, under God, shall have a new birth of freedom -- and that government of the people, by the people, for the people, shall not perish from the earth.</textarea><br />_x000D_
<input type="button" id="countWordsBtn" value="Count Words" />_x000D_
<div id="wordFreq"></div>
_x000D_
_x000D_
_x000D_


There may be a more efficient way to do this, but this is what has worked for me.

function countWords(passedString){
  passedString = passedString.replace(/(^\s*)|(\s*$)/gi, '');
  passedString = passedString.replace(/\s\s+/g, ' '); 
  passedString = passedString.replace(/,/g, ' ');  
  passedString = passedString.replace(/;/g, ' ');
  passedString = passedString.replace(/\//g, ' ');  
  passedString = passedString.replace(/\\/g, ' ');  
  passedString = passedString.replace(/{/g, ' ');
  passedString = passedString.replace(/}/g, ' ');
  passedString = passedString.replace(/\n/g, ' ');  
  passedString = passedString.replace(/\./g, ' '); 
  passedString = passedString.replace(/[\{\}]/g, ' ');
  passedString = passedString.replace(/[\(\)]/g, ' ');
  passedString = passedString.replace(/[[\]]/g, ' ');
  passedString = passedString.replace(/[ ]{2,}/gi, ' ');
  var countWordsBySpaces = passedString.split(' ').length; 
  return countWordsBySpaces;

}

its able to recognise all of the following as separate words:

abc,abc = 2 words,
abc/abc/abc = 3 words (works with forward and backward slashes),
abc.abc = 2 words,
abc[abc]abc = 3 words,
abc;abc = 2 words,

(some other suggestions I've tried count each example above as only 1 x word) it also:

  • ignores all leading and trailing white spaces

  • counts a single-letter followed by a new line, as a word - which I've found some of the suggestions given on this page don't count, for example:
    a
    a
    a
    a
    a
    sometimes gets counted as 0 x words, and other functions only count it as 1 x word, instead of 5 x words)

if anyone has any ideas on how to improve it, or cleaner / more efficient - then please add you 2 cents! Hope This Helps Someone out.


The easiest way I've find so far is to use a regex with split.

_x000D_
_x000D_
var calculate = function() {_x000D_
  var string = document.getElementById('input').value;_x000D_
  var length = string.split(/[^\s]+/).length - 1;_x000D_
  document.getElementById('count').innerHTML = length;_x000D_
};
_x000D_
<textarea id="input">My super text that does 7 words.</textarea>_x000D_
<button onclick="calculate()">Calculate</button>_x000D_
<span id="count">7</span> words
_x000D_
_x000D_
_x000D_


String.prototype.match returns an array, we can then check the length,

I find this method to be most descriptive

var str = 'one two three four five';

str.match(/\w+/g).length;

I think this answer will give all the solutions for:

  1. Number of characters in a given string
  2. Number of words in a given string
  3. Number of lines in a given string

_x000D_
_x000D_
 function NumberOf() { _x000D_
   var string = "Write a piece of code in any language of your choice that computes the total number of characters, words and lines in a given text. \n This is second line. \n This is third line.";_x000D_
_x000D_
   var length = string.length; //No of characters_x000D_
   var words = string.match(/\w+/g).length; //No of words_x000D_
   var lines = string.split(/\r\n|\r|\n/).length; // No of lines_x000D_
_x000D_
   console.log('Number of characters:',length);_x000D_
   console.log('Number of words:',words);_x000D_
   console.log('Number of lines:',lines);_x000D_
_x000D_
_x000D_
}_x000D_
_x000D_
NumberOf();
_x000D_
_x000D_
_x000D_

  1. First you need to find length of the given string by string.length
  2. Then you can find number of words by matching them with string string.match(/\w+/g).length
  3. Finally you can split each line like this string.length(/\r\n|\r|\n/).length

I hope this can help those who are searching for these 3 answers.


The answer given by @7-isnotbad is extremely close, but doesn't count single-word lines. Here's the fix, which seems to account for every possible combination of words, spaces and newlines.

function countWords(s){
    s = s.replace(/\n/g,' '); // newlines to space
    s = s.replace(/(^\s*)|(\s*$)/gi,''); // remove spaces from start + end
    s = s.replace(/[ ]{2,}/gi,' '); // 2 or more spaces to 1
    return s.split(' ').length; 
}

This will handle all of the cases and is as efficient as possible. (You don't want split(' ') unless you know beforehand that there are no spaces of greater length than one.):

var quote = `Of all the talents bestowed upon men, 
              none is so precious as the gift of oratory. 
              He who enjoys it wields a power more durable than that of a great king. 
              He is an independent force in the world. 
              Abandoned by his party, betrayed by his friends, stripped of his offices, 
              whoever can command this power is still formidable.`;

function wordCount(text = '') {
  const words = text.trim().split(/\s+/g);

  if (!words[0].length) {
    return 0;
  }

  return words.length;
};

console.log(WordCount(quote));//59
console.log(WordCount('f'));//1
console.log(WordCount('  f '));//1
console.log(WordCount('   '));//0

One more way to count words in a string. This code counts words that contain only alphanumeric characters and "_", "’", "-", "'" chars.

function countWords(str) {
  var matches = str.match(/[\w\d\’\'-]+/gi);
  return matches ? matches.length : 0;
}

Here's a function that counts number of words in an HTML code:

$(this).val()
    .replace(/((&nbsp;)|(<[^>]*>))+/g, '') // remove html spaces and tags
    .replace(/\s+/g, ' ') // merge multiple spaces into one
    .trim() // trim ending and beginning spaces (yes, this is needed)
    .match(/\s/g) // find all spaces by regex
    .length // get amount of matches