Notes

はじめに

  • これまで、HTMLとCSSを使って簡単なWebページを構築する方法、GitとGitHubを使ってコードの変更を追跡し、他の人と共同作業する方法について説明してきました。また、Pythonプログラミング言語に慣れ、Djangoを使用してWebアプリケーションを作成し始め、Djangoモデルを使用してサイトに情報を保存する方法を学びました。
  • 本日は、新しいプログラミング言語であるJavaScriptを紹介します。

JavaScript

まず、少し前のレッスンの図をもう1度見てみましょう。

Client Server Diagram

ほとんどのオンライン通信では、HTTP要求をサーバーに送信するクライアント/ユーザがあり、サーバーはHTTP応答を送り返します。これまでDjangoを使って作成したPythonコードはすべて、サーバー上で実行されています。JavaScriptを使用すると、クライアント側でコードを実行できます。つまり、実行中にサーバと通信する必要がないため、Webサイトをよりインタラクティブにすることができます。

ページにJavaScriptを追加するために、HTMLページのどこかに <script> タグのペアを追加します。<script> タグを使用して、2つのタグの間に記述するものはすべて、ユーザがサイトを訪れたときに実行したいJavaScriptコードであることをブラウザーに通知します。最初のプログラムは次のようになります。

alert('Hello, world!');

JavaScriptの alert 関数は、ユーザにメッセージを表示し、ユーザはそれを無視できます。これが実際のHTMLドキュメントのどこに収まるかを示すために、JavaScriptを使用した単純なページの例を示します。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Hello</title>
        <script>
            alert('Hello, world!');
        </script>
    </head>
    <body>
        <h1>Hello!</h1>
    </body>
</html>
alert

イベント

Webプログラミングに役立つJavaScriptの機能の1つは、イベント駆動型プログラミングをサポートしていることです。

イベント駆動型プログラミングは、イベントの検出と、イベントが検出されたときに実行する必要があるアクションを中心としたプログラミングパラダイムです。

イベントには、クリックされたボタン、移動されたカーソル、入力された応答、ロードされたページなど、ほとんどのものがあります。ユーザがWebページを操作するために行うことは、ほぼすべてイベントと考えることができます。JavaScriptでは、特定のイベントが発生するのを待ってからコードを実行するイベントリスナを使用します。

上からJavaScriptを hello という関数に変換することから始めましょう。

function hello() {
    alert('Hello, world!')
}

次に、ボタンがクリックされたときにこの関数を実行する方法について説明します。これを行うには、onclick 属性を持つHTMLボタンをページ内に作成します。この属性は、ボタンがクリックされたときに実行される処理をブラウザに指示します。

<button onclick="hello()">Click Here</button>

以上のように変更すると、JavaScriptのコードは、イベントが発生するまで、実行を待機します。

変数

JavaScriptはPythonやC、その他の言語と同じようなプログラミング言語であり、変数を含む他の言語と同じ機能を数多く備えています。JavaScriptで値を割り当てるために使用できるキーワードは3つあります。

  • var:変数をグローバルに定義するために使用されます。
var age = 20;
  • let:関数やループなどの現在のブロックにスコープを制限する変数を定義するために使用されます。
let counter = 1;
  • const:変更されない値を定義するために使用されます。
const PI = 3.14;

変数の使い方の例として、カウンタを追跡するページを見てみましょう。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Count</title>
        <script>
            let counter = 0;
            function count() {
                counter++;
                alert(counter);
            }
        </script>
    </head>
    <body>
        <h1>Hello!</h1>
        <button onclick="count()">Count</button>
    </body>
</html>
counting

クエリセレクタ

JavaScriptを使用すると、警告を通じてメッセージを表示できるだけでなく、ページ上の要素を変更することもできます。これを行うには、まず document.querySelector という関数を導入する必要があります。この関数は、DOMの要素を検索して戻します。たとえば、次のように使用すると、

let heading = document.querySelector('h1');

見出しを抽出します。次に、最近見つけた要素を操作するために、その innerHTML プロパティを変更します。

heading.innerHTML = `Goodbye!`;

Pythonの場合と同様に、JavaScriptの条件 を利用することもできます。たとえば、ヘッダーを常に 「Goodbye!」 に変更するのではなく、Hello!Goodbye! の間を行ったり来たりしたいと思います。その場合、ページは次のようになります。JavaScriptでは、オブジェクトが同じ型であるかどうかもチェックするために、=== を使用して、より厳密に比較をします。通常は、可能な限り === を使用します。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Count</title>
        <script>
            function hello() {
                const header = document.querySelector('h1');
                if (header.innerHTML === 'Hello!') {
                    header.innerHTML = 'Goodbye!';
                }
                else {
                    header.innerHTML = 'Hello!';
                }
            }
        </script>
    </head>
    <body>
        <h1>Hello!</h1>
        <button onclick="hello()">Click Here</button>
    </body>
</html>
toggle

DOMの操作

DOMの操作のアイデアを使って、カウンタページを改善してみましょう。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Count</title>
        <script>
            let counter = 0;
            function count() {
                counter++;
                document.querySelector('h1').innerHTML = counter;
            }
        </script>
    </head>
    <body>
        <h1>0</h1>
        <button onclick="count()">Count</button>
    </body>
</html>
count 2

カウンタが10の倍数になるたびに警告を表示することで、このページをさらに面白くすることができます。この警告では、メッセージをカスタマイズするために文字列をフォーマットします。JavaScriptでは、テンプレートリテラルを使用します。テンプレートリテラルでは、式全体の前後にバッククォート (`) があり、変数を置換したい箇所の前後に$と中括弧があることが必要です。たとえば、count関数を変更します。

function count() {
    counter++;
    document.querySelector('h1').innerHTML = counter;
    
    if (counter % 10 === 0) {
        alert(`Count is now ${counter}`)
    }
}
count with alert

では、このページのデザインを改善する方法をいくつか見てみましょう。まず、CSSを使ったインライン・スタイル設定を避けようとしているのと同じように、可能な限りインラインJavaScriptを避けたいと考えています。カウンタの例でこれを行うには、ページ上のボタンの onclick 属性を変更するスクリプト行を追加し、button タグ内から onclick 属性を削除します。

document.querySelector('button').onclick = count;

ここで注意しなければならないのは、count 関数の後に括弧を付けることによって、count関数を呼び出しているのではなく、単に関数の名前を付けていることです。これにより、ボタンがクリックされたときにのみこの関数を呼び出すように指定します。これが機能するのは、Pythonと同様にJavaScriptが関数型プログラミングをサポートしているため、関数を値そのものとして扱うことができるからです。

上記の変更だけでは十分ではありません。ページを調べてブラウザのコンソールを見ると、次のように表示されます。

error console

このエラーが発生したのは、JavaScriptが document.querySelector('button') を使用して要素を検索したときに、何も見つからなかったためです。これは、ページのロードに少し時間がかかり、ボタンがレンダリングされる前にJavaScriptコードが実行されるためです。これを説明するために、addEventListener 関数を使用して、ページがロードされた後にのみコードが実行されるように指定できます。この関数は、次の2つの引数を取ります。

  1. リスナーイベント (例: 'click' )
  2. イベントを検出したときに実行する機能 (例:上記の hello )

関数を使用して、すべてのコンテンツがロードされた後にのみコードを実行できます。

document.addEventListener('DOMContentLoaded', function() {
    // Some code here
});

上の例では匿名関数を使用していますが、これは名前が与えられることのない関数です。これらをまとめると、JavaScriptは次のようになります。

let counter = 0;

function count() {
    counter++;
    document.querySelector('h1').innerHTML = counter;
    
    if (counter % 10 === 0) {
        alert(`Count is now ${counter}`)
    }
}

document.addEventListener('DOMContentLoaded', function() {
    document.querySelector('button').onclick = count;
});

設計を改善するもう1つの方法は、JavaScriptを別のファイルに移動することです。この方法は、スタイリング用にCSSを別のファイルに配置する方法と非常によく似ています。

  1. すべてのJavaScriptコードを .js ( index.js など) で終わる別のファイルに記述します。
  2. src属性に、この新しいファイルを指定して、<script> タグに追加します。

カウンタページには、次のような counter.html というファイルがあります。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Count</title>
        <script src="counter.js"></script>
    </head>
    <body>
        <h1>0</h1>
        <button>Count</button>
    </body>
</html>

次のような counter.js というファイルがあります。

let counter = 0;
            
function count() {
    counter++;
    document.querySelector('h1').innerHTML = counter;
    
    if (counter % 10 === 0) {
        alert(`Count is now ${counter}`)
    }
}

document.addEventListener('DOMContentLoaded', function() {
    document.querySelector('button').onclick = count;
});

JavaScriptを別のファイルに保存すると、次のようないくつかの理由で便利です。

  • ビジュアルな魅力:個々のHTMLファイルとJavaScriptファイルが読みやすくなります。
  • HTMLファイル間のアクセス:複数のHTMLファイルから、同じJavaScriptを共有できます。
  • コラボレーション:ある人がJavaScriptで作業し、別の人がHTMLで作業できるようになりました。
  • インポート:他の人がすでに作成したJavaScriptライブラリーをインポートすることができます。例えば、Bootstrapには独自のJavaScriptライブラリがあり、サイトをよりインタラクティブにすることができます。

もう少しインタラクティブなページの別の例を見てみましょう。以下では、ユーザが自分の名前を入力してカスタムグリーティングを取得できるページを作成します。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Hello</title>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            document.querySelector('form').onsubmit = function() {
                const name = document.querySelector('#name').value;
                alert(`Hello, ${name}`);
            };
        });
    </script>
</head>
<body>
    <form>
        <input autofocus id="name" placeholder="Name" type="text">
        <input type="submit">
    </form>
</body>
</html>
Greeting Demo

上のページに関する注意:

  • nameの入力フィールドで autofocus フィールドを使用して、ページがロードされるとすぐにカーソルが入力フィールド内に移動するようにします。
  •  document.querySelector 内で #name を使用して、name という id を持つ要素を検索します。この関数では、CSSで使用可能なセレクタと同じセレクタをすべて使用できます。
  • querySelectorで取得した入力フィールドの valueプロパティを使用して、現在入力されている内容を取得します。

JavaScriptを使用してページにHTMLを追加するだけでなく、ページのスタイルを変更することもできます。次のページでは、ボタンを使用して見出しの色を変更します。

<!DOCTYPE html>
<html lang="en">
<head>
     <title>Colors</title>
     <script>
         document.addEventListener('DOMContentLoaded', function() {
            document.querySelectorAll('button').forEach(function(button) {
                button.onclick = function() {
                    document.querySelector("#hello").style.color = button.dataset.color;
                }
            });
         });
     </script>
</head>
<body>
    <h1 id="hello">Hello</h1>
    <button data-color="red">Red</button>
    <button data-color="blue">Blue</button>
    <button data-color="green">Green</button>
</body>
</html>
Color demo

上のページでいくつかの注意事項があります。

  • style.SOMETHING 属性を使用して要素のスタイルを変更します。
  • data-SOMETHING 属性を使用して、HTML要素にデータを割り当てます。後で、要素の dataset プロパティを使用してJavaScriptのデータにアクセスできます。
  • クエリーに一致するすべての要素を含むノードリスト (PythonのリストやJavaScriptの配列に似ています)を取得するには、querySelectorAll 関数を使用します。
  • JavaScriptのforEach 関数は、別の関数を指定でき、その関数をリストまたは配列の各要素に適用します。

JavaScriptコンソール

コンソールは、小さなコードをテストしたり、デバッグしたりするのに便利なツールです。JavaScriptコードを記述して実行するには、Webブラウザで要素を調べ、 「console」 をクリックします (正確な手順は、ブラウザーによって異なる可能性があります) 。デバッグに役立つツールの1つに、console.log 関数を使用して実行できるコンソールへの出力があります。たとえば、上記の colors.html ページでは、次の行を追加できます。

console.log(document.querySelectorAll('button'));

コンソールでは次のようになります。

node list

アロー関数

これまで見てきた従来の関数表記に加えて、JavaScriptではアロー関数を使うことができるようになり、ここでは入力 (入力がない場合は括弧) の後に => が続き、その後に実行するコードが続きます。たとえば、匿名のアロー関数を使用するように上記のスクリプトを変更できます。

document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll('button').forEach(button => {
        button.onclick = () => {
            document.querySelector("#hello").style.color = button.dataset.color;
        }
    });
});

この count 関数の書き換えのように、アローを使用する名前付き関数を使用することもできます。

count = () => {
    counter++;
    document.querySelector('h1').innerHTML = counter;
    
    if (counter % 10 === 0) {
        alert(`Count is now ${counter}`)
    }
}

他にどんなイベントを利用できるか理解を深めるために、3つの個別のボタンの代わりにドロップダウンメニューを使用してカラースイッチャを実装する方法を見てみましょう。onchange 属性を使用すると、select 要素の変更を検出できます。JavaScriptでは、thisは使用されるコンテキストに基づいて変化するキーワードです。イベントハンドラの場合は、this はイベントをトリガしたオブジェクトを参照します。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Colors</title>
        <script>
            document.addEventListener('DOMContentLoaded', function() {
                document.querySelector('select').onchange = function() {
                    document.querySelector('#hello').style.color = this.value;
                }
            });
        </script>
    </head>
    <body>
        <h1 id="hello">Hello</h1>
        <select>
            <option value="black">Black</option>
            <option value="red">Red</option>
            <option value="blue">Blue</option>
            <option value="green">Green</option>
        </select>

    </body>
</html>
colors with dropdown

JavaScriptで検出できるイベントは他にもたくさんあり、その中には以下のような一般的なものがあります。

  • onclick
  • onmouseover
  • onkeydown
  • onkeyup
  • onload
  • onblur

TODOリスト

この講義で学んだことをまとめるために、完全にJavaScriptでTODOリストを作成してみましょう。まず、ページのHTMLレイアウトを作成します。以下では、順序付けされていないリスト用のスペースを残していますが、まだ何も追加していません。また、JavaScriptを記述する tasks.js へのリンクを追加しています。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Tasks</title>
        <script src="tasks.js"></script>
    </head>
    <body>
        <h1>Tasks</h1>
        <ul id="tasks"></ul>
        <form>
            <input id="task" placeholder = "New Task" type="text">
            <input id="submit" type="submit">
        </form>
    </body>
</html>

次のコードが tasks.js に保存できるコードです。以下にいくつかの注意点を示します。

  • このコードは、レッスンのコードとは少し異なります。ここでは、送信ボタンとタスク入力フィールドのクエリーを最初に1回だけ実行し、これら2つの値を変数 submit と newTask に格納します。
  • ボタンを有効/無効にするには、ボタンの disabled 属性を false/true に設定します。
  • JavaScriptでは、.length を使用して文字列や配列などのオブジェクトの長さを取得します。
  • スクリプトの最後に、return false という行を追加します。これにより、現在のページのリロードや新しいページへのリダイレクトを伴うフォームのデフォルト送信が防止されます。
  • JavaScriptでは、createElement 関数を使用してHTML要素を作成できます。次に、append 関数を使用してこれらの要素をDOMに追加します。
// Wait for page to load
document.addEventListener('DOMContentLoaded', function() {

    // Select the submit button and input to be used later
    const submit = document.querySelector('#submit');
    const newTask = document.querySelector('#task');

    // Disable submit button by default:
    submit.disabled = true;

    // Listen for input to be typed into the input field
    newTask.onkeyup = () => {
        if (newTask.value.length > 0) {
            submit.disabled = false;
        }
        else {
            submit.disabled = true;
        }
    }

    // Listen for submission of form
    document.querySelector('form').onsubmit = () => {

        // Find the task the user just submitted
        const task = newTask.value;

        // Create a list item for the new task and add the task to it
        const li = document.createElement('li');
        li.innerHTML = task;

        // Add new element to our unordered list:
        document.querySelector('#tasks').append(li);

        // Clear out input field:
        newTask.value = '';

        // Disable the submit button again:
        submit.disabled = true;

        // Stop form from submitting
        return false;
    }
});
Tasks demo

インターバル (間隔)

イベントがトリガされたときに関数が実行されるように指定するだけでなく、一定時間後に関数が実行されるように設定することもできます。たとえば、カウンタページのスクリプトに戻り、ユーザが何もクリックしなくてもカウンタが1秒ごとに増加するように間隔を追加します。これを行うには、setInterval関数を使用します。この関数は、実行する関数と関数の実行間隔 (ミリ秒単位) を引数として取ります。

let counter = 0;
            
function count() {
    counter++;
    document.querySelector('h1').innerHTML = counter;
}

document.addEventListener('DOMContentLoaded', function() {
    document.querySelector('button').onclick = count;

    setInterval(count, 1000);
});
count auto

ローカルストレージ

これまでのところ、私たちのすべてのサイトで気になるのは、ページをリロードするたびに、私たちのすべての情報が失われていることです。見出しの色が黒に戻り、カウンタが0に戻り、すべてのタスクが消去されます。開発者が、このような動作を意図している場合もありますが、ユーザがサイトに戻ったときに使用できる情報を保存できるようにしたい場合もあります。

これを行う1つの方法は、ローカルストレージを使用するか、後でアクセスできるようにユーザのWebブラウザに情報を保存することです。この情報は、Pythonの辞書型のように、キーと値のペアのセットとして格納されます。ローカルストレージを使用するには、次の2つの主要な機能を使用します。

  • localStorage.getItem(key):この関数は、指定されたキーを持つローカルストレージ内のエントリを検索し、そのキーに関連付けられた値を返します。
  • localStorage.setItem(key, value):この関数は、キーを新しい値に関連付けて、ローカルストレージ内にキーとエントリを設定します。

これらの新しい機能を使用してカウンタを更新する方法を見てみましょう。

// Check if there is already a vlaue in local storage
if (!localStorage.getItem('counter')) {

    // If not, set the counter to 0 in local storage
    localStorage.setItem('counter', 0);
}
            
function count() {
    // Retrieve counter value from local storage
    let counter = localStorage.getItem('counter');

    // update counter
    counter++;
    document.querySelector('h1').innerHTML = counter;

    // Store counter in local storage
    localStorage.setItem('counter', counter);
}

document.addEventListener('DOMContentLoaded', function() {
    // Set heading to the current value inside local storage
    document.querySelector('h1').innerHTML = localStorage.getItem('counter');
    document.querySelector('button').onclick = count;
});

APIs

JavaScript オブジェクト

JavaScriptオブジェクトは、キーと値のペアを格納できる点でPythonの辞書型に非常に似ています。たとえば、Harry Potterを表すJavaScriptオブジェクトを作成できます。

let person = {
    first: 'Harry',
    last: 'Potter'
};

次に、括弧またはドット表記のいずれかを使用して、このオブジェクトの一部にアクセスしたり変更したりできます。

Harry Potter

JavaScriptオブジェクトが本当に役立つものの1つは、特にAPIを使用する場合に、あるサイトから別のサイトにデータを転送することです。

API (Application Programming Interface) は、2つの異なるアプリケーション間の構造化された形式の通信です。

例えば、アプリケーションでGoogleマップやAmazon、あるいは何らかの気象サービスから情報を取得したいとします。そのためには、構造化されたデータをJSON (JavaScriptオブジェクト表記) 形式で返すサービスのAPIを呼び出す必要があります。たとえば、JSON形式のフライトは次のようになります。

{
    "origin": "New York",
    "destination": "London",
    "duration": 415
}

JSON内の値は、上記の例のように文字列と数値だけである必要はありません。リストや他のJavaScriptオブジェクトを保存することもできます。

{
    "origin": {
        "city": "New York",
        "code": "JFK"
    },
    "destination": {
        "city": "London",
        "code": "LHR"
    },
    "duration": 415
}

為替レート

アプリケーションでAPIを使用する方法を示すために、2つの通貨間の為替レートを検索できるアプリケーションを作成してみましょう。この演習では、ヨーロッパ中央銀行の為替レートAPIを使用します。Webサイトにアクセスすると、APIのドキュメントが表示されます。このドキュメントは、通常、APIを使用する場合の開始点として適しています。このAPIをテストするには、https://api.exchangeratesapi.io/latest?base=USDにアクセスします。このページにアクセスすると、米ドルと他の多くの通貨間の為替レートがJSON形式で表示されます。URLのGETパラメータを USD から他の通貨コードに変更して、取得するレートを変更することもできます。

currency.html という新しいHTMLファイルを作成し、それをJavaScriptファイルにリンクし、本文を空のままにして、このAPIをアプリケーションに実装する方法を見てみましょう。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Currency Exchange</title>
        <script src="currency.js"></script>
    </head>
    <body></body>
</html>

ここでは、AJAX (Asynchronous JavaScript And XML) と呼ばれるものを使用して、ページがロードされた後でも外部ページから情報にアクセスできるようにします。そのためには、HTTPリクエストを送信できるfetch 関数を使用します。fetch 関数はpromiseを返します。ここでは、どのようなpromiseがあるのかの詳細については説明しませんが、これはある時点で返される値であり、すぐに値が返されるとは限らないと考えてください。私たちは、response を得たときに何をすべきかを記述した .then 属性を与えることによって、promise を処理します。以下のコード・スニペットは、応答をコンソールに記録します。

document.addEventListener('DOMContentLoaded', function() {
    // Send a GET request to the URL
    fetch('https://api.exchangeratesapi.io/latest?base=USD')
    // Put response into json form
    .then(response => response.json())
    .then(data => {
        // Log data to the console
        console.log(data);
    });
});
Currency log

上記のコードで重要な点は、.then の引数が常に関数であるということです。response 変数と data 変数を作成しているように見えますが、これらの変数は2つの匿名関数のパラメーターにすぎません。

このデータを単にログに記録するのではなく、次のコードのように、JavaScriptを使用して画面にメッセージを表示できます。

document.addEventListener('DOMContentLoaded', function() {
    // Send a GET request to the URL
    fetch('https://api.exchangeratesapi.io/latest?base=USD')
    // Put response into json form
    .then(response => response.json())
    .then(data => {

        // Get rate from data
        const rate = data.rates.EUR;

        // Display message on the screen
        document.querySelector('body').innerHTML = `1 USD is equal to ${rate.toFixed(3)} EUR.`;
    });
});
Currency

では、ユーザが見たい通貨を選択できるようにすることで、サイトをもう少しインタラクティブにしてみましょう。まず、HTMLを変更して、ユーザが通貨を入力できるようにします。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Currency Exchange</title>
        <script src="currency.js"></script>
    </head>
    <body>
        <form>
            <input id="currency" placeholder="Currency" type="text">
            <input type="submit" value="Convert">
        </form>
        <div id="result"></div>
    </body>
</html>

次に、JavaScriptを変更して、フォームが送信されたときにのみ変更されるようにし、ユーザの入力を考慮するようにします。エラーチェックもここに追加します。

document.addEventListener('DOMContentLoaded', function() {
    document.querySelector('form').onsubmit = function() {

        // Send a GET request to the URL
        fetch('https://api.exchangeratesapi.io/latest?base=USD')
        // Put response into json form
        .then(response => response.json())
        .then(data => {
            // Get currency from user input and convert to upper case
            const currency = document.querySelector('#currency').value.toUpperCase();

            // Get rate from data
            const rate = data.rates[currency];

            // Check if currency is valid:
            if (rate !== undefined) {
                // Display exchange on the screen
                document.querySelector('#result').innerHTML = `1 USD is equal to ${rate.toFixed(3)} ${currency}.`;
            }
            else {
                // Display error on the screen
                document.querySelector('#result').innerHTML = 'Invalid Currency.';
            }
        })
        // Catch any errors and log them to the console
        .catch(error => {
            console.log('Error:', error);
        });
        // Prevent default submission
        return false;
    }
});
Echange demo

これでこのレッスンは終わりです!次回は、JavaScriptを使用してさらに魅力的なユーザインターフェースを作成する作業に取り掛かります。