JavaScript単語カウンター

概要:このチュートリアルでは、Vanilla JavaScriptを使用して単語カウンターアプリケーションを構築する方法を学習します。

これから構築する単語カウンターアプリはこちらです。

プロジェクト構造の作成

まず、word-counterというプロジェクトフォルダを作成します。

次に、word-counterプロジェクトの下に、CSSファイルとJavaScriptファイルをそれぞれ格納するcssフォルダとjsフォルダを作成します。

3番目に、cssフォルダ内にstyle.cssファイルを作成し、jsフォルダ内にword-counter.jsapp.jsという2つのJavaScriptファイルを作成します。

最後に、プロジェクトのルートフォルダにindex.htmlファイルを作成します。

最終的なプロジェクトフォルダ構造は次のようになります

HTMLファイルの作成

まず、index.htmlファイルを編集し、CSSファイルとJavaScriptファイルをindex.htmlファイルに配置します

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Word Counter</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
   
    <script src="js/word-counter.js"></script>
    <script src="js/app.js"></script>
</body>
</html>Code language: HTML, XML (xml)

WordCounterアプリには、シンプルな<textarea>要素があります。テキストを入力すると、入力した文字数と単語数が表示されます。

そのためには、<textarea>要素と<div>要素が必要です

  • <textarea>要素を使用すると、テキストを入力できます。
  • <div>要素には、<textarea>要素に入力された文字数と単語数が表示されます。

デフォルトでは、<div>要素には0文字と0単語が表示されます。

次に、開始<body>タグの後、最初の<script>タグの前に、<textarea>要素と<div>要素をindex.htmlファイルに追加します

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Word Counter</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
   
<h1>Word Counter</h1>
    <label for="text">Enter some text below:</label>
    <textarea id="text" rows="10" cols="60"></textarea>
    <div id="stat">You've written 0 words and 0 characters.</div>
    <script src="js/word-counter.js"></script>
    <script src="js/app.js"></script>
</body>
</html>Code language: HTML, XML (xml)

WordCounterクラスの作成

まず、word-counter.jsファイルにWordCounterクラスを作成します

class WordCounter {
}Code language: JavaScript (javascript)

WordCounterクラスは、<textarea>要素を受け入れます。 <textarea>要素のinputイベントをリッスンし、<textarea>要素に含まれる文字数と単語数を計算します。

次に、WordCounterクラスに`constructor`を追加します。 `constructor`は`<textarea>`要素を受け入れます。

`constructor`内では、クラスの`inputText`プロパティを`inputText`引数に初期化し、`input`イベントリスナーを`inputText`要素にアタッチします

class WordCounter {
    constructor(inputText) {
        this.inputText = inputText;
        this.inputText.addEventListener('input', this.count);
    }
    count(){
    }
}Code language: JavaScript (javascript)

`this.count()`メソッドは、`input`イベントが発生するたびに実行されます。 `count()`メソッドのロジックを実装するために、後で戻ります。

3番目に、文字数と単語数を計算する新しいメソッドを`WordCounter`クラスに追加します

class WordCounter {
    constructor(inputText) {
        this.inputText = inputText;
        this.inputText.addEventListener('input', this.count);
    }
    count(){
    }
    getWordStat(str) {
        let matches = str.match(/\S+/g);
        return {
            characters: str.length,
            words: matches ? matches.length : 0,
        };
    }
}Code language: JavaScript (javascript)

`getWordStat()`メソッドは、正規表現 `/\S/g`を使用して、文字列の単語数を返します。また、入力文字列`str`の文字列`length`プロパティを使用して、文字数を取得します。

3番目に、`count()`メソッドは`getWordStat()`を呼び出して、`inputText`要素の単語数と文字数を計算する必要があります。

`<textarea>`要素のテキストを取得するには、その`value`プロパティを使用します

...
count() {
   let wordStat = this.getWordStat(this.inputText.value.trim());
   // how to expose the wordStat to the outside
   // ..
}Code language: JavaScript (javascript)

また、`count()`メソッドは、単語数と文字数を外部と通信する必要があります。

これを行うには、コールバックカスタムイベントの2つのオプションがあります。このチュートリアルでは、カスタムイベントを使用します。

コールバックの使用方法を知りたい場合は、カウントダウンタイマーのチュートリアルをご覧ください。

4番目に、`emitEvent`という新しいメソッドを`WordCounter`クラスに追加します

emitEvent(wordStat) {
        // Create count event
        let countEvent = new CustomEvent('count', {
            bubbles: true,
            cancelable: true,
            detail: {
                wordStat
            }
        });
        // dispatch the count event
        this.inputText.dispatchEvent(countEvent);
}Code language: JavaScript (javascript)

`emitEvent()`メソッドは、`wordStat`オブジェクトを受け入れます。メソッド内では、`CustomEvent`コンストラクターを使用して`inputText`要素のカスタムイベント`count`を作成し、`dispatchEvent`メソッドを使用して`count`イベントをディスパッチします。

後で、`count`イベントにイベントリスナーをアタッチし、`event.detail.wordStat`構文を使用して`wordStat`オブジェクトにアクセスします。

`emitEvent()`は、`input`イベントが発生するたびに呼び出す必要があります。したがって、`count()`メソッド内で`emitEvent()`を呼び出します

count(){
   let wordStat = this.getWordStat(this.inputText.value.trim());
   this.emitEvent(wordStat);
}Code language: JavaScript (javascript)

`WordCounter`クラスは次のようになります

class WordCounter {
    constructor(inputText) {
        this.inputText = inputText;
        this.inputText.addEventListener('input', this.count);
    }
    count(){
        let wordStat = this.getWordStat(this.inputText.value.trim());
        this.emitEvent(wordStat);
    }

    emitEvent(wordStat) {
        // Create count event
        let countEvent = new CustomEvent('count', {
            bubbles: true,
            cancelable: true,
            detail: {
                wordStat
            }
        });
        // dispatch the count event
        this.inputText.dispatchEvent(countEvent);

    }
    getWordStat(str) {
        let matches = str.match(/\S+/g);
        return {
            characters: str.length,
            words: matches ? matches.length : 0,
        };
    }
}Code language: JavaScript (javascript)

app.jsファイルにロジックを追加する

まず、`querySelector()`メソッドを使用して、`<textarea>`要素と`<div>`要素を選択します

const inputText = document.querySelector('#text');
const statElem = document.querySelector('#stat');
Code language: JavaScript (javascript)

次に、`WordCounter`クラスの新しいインスタンスを作成し、`inputText`要素をそのコンストラクターに渡します

new WordCounter(inputText);Code language: JavaScript (javascript)

3番目に、単語数と文字数を`statElem`要素に更新する`render()`という新しい関数を定義します。

`render()`関数は、カスタムイベントオブジェクトを受け入れます

const render = (event) => {
    statElem.innerHTML = `<p>You've written <span class="highlight">${event.detail.wordStat.words} words</span> 
        and <span class="highlight">${event.detail.wordStat.characters} characters</span>.</p>`;
}
Code language: HTML, XML (xml)

4番目に、`count`イベントにイベントリスナーを追加し、`count`イベントが発生するたびに`render()`メソッドを実行します

inputText.addEventListener('count', render);Code language: JavaScript (javascript)

`app.js`は次のようになります

const inputText = document.querySelector('#text');
const statElem = document.querySelector('#stat');

// create a new instance of WordCounter
new WordCounter(inputText);


const render = (event) => {
    statElem.innerHTML = `<p>You've written <span class="highlight">${event.detail.wordStat.words} words</span> 
        and <span class="highlight">${event.detail.wordStat.characters} characters</span>.</p>`;
}

inputText.addEventListener('count', render);Code language: JavaScript (javascript)

ここで、Webブラウザで`index.html`ファイルを開くと、次のエラーが表示されます

Uncaught TypeError: Cannot read property 'value' of undefined at HTMLTextAreaElement.countCode language: HTML, XML (xml)

問題は`WordCounter`クラスの`count()`メソッドで発生しました

`this.inputText`が`undefined`であることが示されています。したがって、`this.inputText`の`value`プロパティにアクセスするとエラーが発生します。

この問題を解決する

`inputText`要素で`input`イベントが発生すると、`count()`メソッドが実行されます。

`count()`メソッドを実行するオブジェクトは、`WordCounter`クラスのインスタンスではなく、`inputText`オブジェクトです。

これは、`count()`メソッド内で、`this`値が`WordCounter`オブジェクトではなく`inputText`要素を参照していることを意味します。

これを証明するために、次のように`count()`メソッド内で`this`値をログに記録できます

count() {
    console.log(this); 
}Code language: JavaScript (javascript)

... `index.html`をもう一度更新すると、`<textarea>`にテキストを入力するたびに、コンソールに`<textarea>`要素が表示されます

<textarea id="text" rows="10" cols="60"></textarea>
Code language: HTML, XML (xml)

`count()`メソッド内の`this`値は`<textarea>`要素を参照しているため、`inputText`プロパティがありません。また、`emitEvent()`メソッドもありません。

この問題を解決するには、次のようにイベントリスナーをアロー関数に変更する必要があります

constructor(inputText) {
   this.inputText = inputText;
    this.inputText.addEventListener('input', () => {
        this.count();
    });
}Code language: JavaScript (javascript)

アロー関数を使用すると、`this`値は周囲のブロックのオブジェクト(この場合は`WordCounter`)を参照します。つまり、`count()`メソッドで`WordCounter`のすべてのプロパティとメソッドにアクセスできます。

最終的な`WordCounter`クラスは次のようになります

class WordCounter {
    constructor(inputText) {
        this.inputText = inputText;
        this.inputText.addEventListener('input', () => {
            this.count();
        });
    }
    count() {
        let wordStat = this.getWordStat(this.inputText.value.trim());
        this.emitEvent(wordStat);
    }

    emitEvent(wordStat) {
        // Create count event
        let countEvent = new CustomEvent('count', {
            bubbles: true,
            cancelable: true,
            detail: {
                wordStat
            }
        });
        // dispatch the count event
        this.inputText.dispatchEvent(countEvent);

    }
    getWordStat(str) {
        let matches = str.match(/\S+/g);
        return {
            characters: str.length,
            words: matches ? matches.length : 0,
        };
    }
}Code language: JavaScript (javascript)

単語カウンターアプリの動作を確認するには、ここをクリックしてください。

まとめ

このチュートリアルでは、Vanilla JavaScriptを使用して単語カウンターアプリを開発する方法を学習しました。主なポイントは次のとおりです

  • カスタムイベントを作成および発行する方法。
  • アロー関数を使用して`this`の問題を解決する方法。
このチュートリアルは役に立ちましたか?