概要:このチュートリアルでは、Vanilla JavaScriptを使用して単語カウンターアプリケーションを構築する方法を学習します。
これから構築する単語カウンターアプリはこちらです。
プロジェクト構造の作成
まず、word-counter
というプロジェクトフォルダを作成します。
次に、word-counter
プロジェクトの下に、CSSファイルとJavaScriptファイルをそれぞれ格納するcss
フォルダとjs
フォルダを作成します。
3番目に、css
フォルダ内にstyle.css
ファイルを作成し、js
フォルダ内にword-counter.js
とapp.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.count
Code 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`の問題を解決する方法。