Skip to main content

ブラウザーの JavaScript について

学習の目的

この単元を完了すると、次のことができるようになります。

  • JavaScript とブラウザーの相互作用について説明する。
  • Web API の役割を説明する。
  • ドキュメントオブジェクトモデルの基本的な API を使用する。
  • Lightning コンポーネントで DOM を抽象化する方法について説明する。

ブラウザーでの JavaScript の機能

JavaScript のランタイムエンジンと言語について簡単に学習しました。また、JavaScript を記述する人なら誰もが知っておくべき事項もいくつか確認しました。このモジュールでは、JavaScript とブラウザーの関係、特に Web ページが JavaScript でどのように表示されるのかを中心に説明します。 

JavaScript ランタイムはブラウザーではなく、ブラウザーは JavaScript ではない

JavaScript ランタイムエンジンはさまざまな場所で実行されますが、通常はブラウザーでホストされます。では、JavaScript とブラウザーは何が違うのでしょうか? 

ブラウザーの主な役割は、Web サーバーのクライアントとして機能することです。数種類のプロトコルのいずれかを使用して (通常は HTTP/HTTPS)、インターネット経由でリソースを要求します。サーバーから一定のリソースが届いたら、ブラウザーはそれを利用して何かを実行する必要があります。少なくとも HTML や CSS はページに変換されます。リソースに JavaScript が含まれる場合は、ブラウザーが JavaScript ランタイムエンジンにアクセスしてそのコードを解析して評価し、実行します。 

同様に、スクリプトが実行されている間に、JavaScript からブラウザーにアクセスして、Web ページを変更したり、ローカル環境と対話したり、他の Web リソースと対話したりすることもあります。

API を備えた JavaScript エンジン

こうした相互作用を機能させるために、ブラウザーは API を表示します。実のところ、人々がクライアント側の JavaScript と思っているものの大半は、実際には以下のような API です。 

Web API

標準

ブラウザーと対話する API があっても驚くことではありません。他のすべてのプログラミング言語やプラットフォームが、それを実行している環境と対話する API を表示するのと同様に、ブラウザーも同じことを行います。 

ブラウザーのインターネットトラフィックの半分以上は Google Chrome から来ていますが、残りの約 30 パーセントは他の 5 つのブラウザーからのトラフィックです。こうしたことから API に対する Web 標準の重要性が強調され、そのお陰で JavaScript も一度作成すればどこでも実行できるようになっています。 

図式への Web API の追加

以前に JavaScript ランタイムの図式を作成しました。 

  • シングルスレッドを使用して要求がスタック上で実行されます。
  • 要求に対するすべての同期ロジックの実行が完了するまで、その要求がスレッドを保有します。
  • 新しい要求はスレッドの準備ができるまでキューに入れられます。
  • スレッドを使用可能になった時点で、イベントループがキューに入った次の要求をスタックに移します。

ここで、JavaScript の図式に Web API を追加します。これらの API によって JavaScript のコア言語が拡張されます。API は、昨今の Web のユーザーエクスペリエンスに欠かせない作業の大半を実行するインターフェースを表示します。たとえば、ブラウザーの API は次のことを実行できます。 

  • ブラウザーに表示される現在のページの構造と対話する (ドキュメントオブジェクトモデル (DOM) API)。
  • 現在のページを開いたまま、サーバーに非同期要求を実行する (Fetch API)。
  • 音声、動画、グラフィックと対話する。
  • ブラウザーに表示されたデバイス機能 (地理位置、デバイスの方向、クライアント側のデータストレージ) と対話する。

ほとんどの場合、キューに新しい要求を追加するのはこれらの API です。 

メモ

ここでは、JavaScript エコシステムの重要な要素にあえて触れていません。つまり、ブラウザーの機能を抽象化や拡張するサードパーティの API や JavaScript のライブラリのことです。これらの要素は、JavaScript を自在に使用するうえで重要な役割を果たします。 

Salesforce と関連する内容を学習するという目的上、ここでは当社の JavaScript フレームワークである Lightning コンポーネントフレームワークについて説明します。特に、Lightning プラットフォーム上の JavaScript について説明する場合、このモジュールは Lightning Web コンポーネント開発モデルに対応しています。

例を挙げてみましょう。フォームが設定された Web ページ上にメモのリストがあるとします。フォームのいくつかの項目に記入し、ボタンをクリックしてそのデータを保存すると、リストに新しい項目が追加されます。試しに、この新しいレコードをローカルにキャッシュして、このレコードに関する今後の要求が加速されるようにします。 

このシンプルなユースケースでも、ブラウザーの次の API との対話があります。

  • Fetch API (レコードを保存)
  • DOM API (新しい項目を HTML リストに追加)
  • クライアント側のデータストレージ (データをローカルにキャッシュ)

ではここで、JavaScript 内でユーザーに示される内容を表す DOM API を詳しく見ていきましょう。 

ドキュメントオブジェクトモデル

DOM: JavaScript 上の各自のページ

ブラウザーがページを要求して受信すると、ブラウザーは HTML を解析して、そのページの説明 (モデル) を作成します。このモデルを使用して、ブラウザーのビューポートにページが描画されます。このページは DOM を介して JavaScript にも表示されます。 

DOM を 1 本の木と考えます。出発点の根となるのがブラウザーの表示機能、つまりウィンドウです。そこから、ページが window.document にカプセル化され、そのページの本文が window.document.body にカプセル化されます。そして、ページ上に表されたコンテンツの各部分が枝として広がります。Web ページの大半は極めて複雑な木で、この階層の末端にあるのが葉であるリーフノードです。 

API である DOM は膨大ですが、この大木の各部に触れることができます。DOM はまた、その相互作用を最適化する多数の手段を備えています。jsbin.com の次のシンプルなを見てみましょう。次のような機能があります。

  • 入力項目
  • [Add Item (項目を追加)] ボタン
  • 順序なしリスト

このボタンをクリックするたびに、コードが入力項目にアクセスしてその値を読み取り、テキストノードに変換してから、新しい li 要素を作成し、li ノードにこのテキストノードを挿入して、最後に新しい li-and-text ノードを ul に貼り付けます。 

メモ

さらに、これまでに説明したいくつかの原則を実践してみたいという方は、jsbin を変更して次のことができるかどうか確認してください。

  • テキスト入力にデータがあるかどうかを確認します。
  • ない場合は、新しい li をリストに追加しないでください。
  • 新しい li が追加されたら、テキスト入力をクリアします。

どうなったか気になってしかたがない場合は、変更済みのコードをチェックします。

ご覧のとおり、これほどシンプルな例でも、開発者がかなりの数の手動手順を実行する必要があります。そして、まだデータをサーバーに保存もしていなければ、他の方法でサーバーと対話することもしていません。このため、対話型ページについては JavaScript のライブラリ (reactjs、jQuery) やフレームワーク (Angular、vuejs) が標準になっています。こうしたフレームワークは、DOM の相互作用を抽象化して単純化し、欠落している機能に自動的にポリフィルを適用します。Lightning コンポーネントフレームワークも同様です。 

Shadow DOM を使用したカプセル化

DOM API は柔軟で、機能が充実しています。比較的シンプルな JavaScript を使用して、UI の外観や動作、UI で呼び出されるアクションを簡単に変更できます。 

けれども、落とし穴があります。DOM モデルでは、UI 要素をカプセル化して誤った変更 (または意図的や悪意のある変更) から守ることが難しくなります。 

このため、Shadow DOM 標準が開発されました。Shadow DOM は UI 機能の特定の部分の周囲に境界を設けるものです。この境界によって親が子の要素や CSS を変更できなくなります。また、境界を越えて伝播したイベントのターゲット範囲を強制的に変更して、親が Shadow DOM の境界を越えて到達することを防止します。

Lightning Web コンポーネントと DOM

メモ

これ以降の各単元には、Lightning Web コンポーネントとの結び付きを具体的に説明するいくつかの例を示す同様のセクションがあります。ここで学習した内容を適用したい場合は、Trailhead の「Lightning Web コンポーネントの基本」モジュールを受講する絶好の機会です。

DOM の自動更新

Lightning コンポーネントの基本的な価値は、Salesforce データに基づいてカスタム UI を構築することです。この点は DOM 操作に関係しています。DOM 自体が Salesforce データによって決定されるためです。 

以下のコンポーネントは、この原則を最もシンプルな形で示しています。

データに基づく UI のあるシンプルなコンポーネント。「This text came from a JS prop (このテキストは JS プロパティから取り込まれました)」というテキストが表示されています。以下は、このコンポーネントのコードです。

// JavaScript Module: demo.js

import { LightningElement } from 'lwc';
export default class Demo extends LightningElement {
    text = 'This text came from a JS prop';
}
<!-- HTML Template: demo.html -->
<template>
    <lightning-card title="Basic DOM Example" icon-name="utility:hierarchy">
        <div class="slds-card__body slds-card__body-inner">
            <p>
                <lightning-formatted-text value={text}></lightning-formatted-text>
            </p>
        </div>
    </lightning-card>
</template>

最初の一定のコンテキスト: どの Lightning Web コンポーネントも JavaScript モジュールが必要です。コンポーネントにユーザーに表示される要素がある場合は、HTML テンプレートも必要です。大半のコンポーネントは、JavaScript と HTML ファイルをバンドルとしてまとめています。 

Lightning Web コンポーネントでは、データを容易に UI に表示できます。たとえば、上記のコードは JavaScript の text プロパティを使用してハードコード化された文字列を保存します。<lightning-formatted-text> タグでは、value 属性が JavaScript の text プロパティにバインドされ、UI にデータが表示されます。

ところで、ユーザーが UI を操作したときに変化させるにはどうすればよいのでしょうか? この例を拡張して、変化させることができます。

以下はそのコードです。

// JavaScript Module demo2.js

import { LightningElement } from 'lwc';
export default class Demo2 extends LightningElement {
    text = '';
    handleChange(event){
        this.text = event.target.value;
    }
}
<!-- HTML Template demo2.html -->
<template>
<lightning-card title="Basic DOM Example" icon-name="utility:hierarchy">
<div class="slds-card__body slds-card__body-inner">
<p>
<lightning-formatted-text value={text}></lightning-formatted-text>
</p>
<p>
<lightning-input onchange={handleChange} label="Input Text" value={text}></lightning-input>
</p>
</div>
</lightning-card>
</template>

Lightning Web コンポーネントでは、JavaScript クラスのプロパティはリアクティブです。つまり、プロパティの値が割り当てられ (または再割り当てされ)、プロパティがテンプレートで使用されると、コンポーネントの DOM によって新しい値が再レンダリングされ、表示されます。

このテンプレートに <lightning-input> が追加されています。データの表示で大切なのは text プロパティのバインドですが、onchange 属性にも注意します。JavaScript クラスに追加されていた handleChange 関数が、ここで change イベントによってトリガーされるようになっています。ユーザーによって値が変更されるたびに text の値が更新されます。テキストが更新されると、DOM も更新されます。  

これは架空の例ですが、暗黙の DOM の変更を開発者が簡単に実装できるようにする機能が Lightning Web コンポーネントにどのように表示されるかを示しています。

明示的な DOM 操作

Lightning Web コンポーネントを構築するときに手動の DOM 操作を最初の手段にすべきではありませんが、高度なコンポーネントではこの手法が必要になることがあります。これはより高度なトピックになりますが、DOM の説明の締めくくりとして、手動の DOM 操作がどのように機能するかを多少なりとも知っておく価値があります。 

条件付き表示

Lightning Web コンポーネントを記述するときに、特定の条件が満たされたときにのみ表示される UI のセクションを明示的に定義できます。具体的には、ネストされた template タグと一緒に lwc:iflwc:elseiflwc:else ディレクティブを使用します。 

//conditionalButton.js

import { LightningElement } from 'lwc';
export default class ConditionalButton extends LightningElement {
show = true;
handleClick(){
this.show = !this.show;
}
}
//conditionalButton.html
<template>
<lightning-card title="Conditional Rendering with Button" icon-name="utility:hierarchy">
<div class="slds-card__body slds-card__body-inner">
<p>
<template lwc:if={show}>
Peek-a-boo!
</template>
<template lwc:else>
I'm hiding!
</template>
</p>
<p>
<lightning-button onclick={handleClick} variant="neutral" label="Switch"></lightning-button>
</p>
</div>
</lightning-card>
</template>

このサンプルコードでは、show プロパティの値が true の場合に、Peek-a-boo! (いないいないばー!) というテキストが表示されます。show が false に設定されている場合は、I'm hiding! (隠れてますよ!) というテキストが表示されます。ボタンをクリックすると、show プロパティの値が切り替わります。

DOM を手動で操作する他の方法

一定の状況では、表示 CSS スタイルを使用して DOM 要素の表示を操作しても構いません。この方法は Lightning Web コンポーネントで通常どおりに機能しますが、この特定のモジュールの範疇外です。 

リソース

無料で学習を続けましょう!
続けるにはアカウントにサインアップしてください。
サインアップすると次のような機能が利用できるようになります。
  • 各自のキャリア目標に合わせてパーソナライズされたおすすめが表示される
  • ハンズオン Challenge やテストでスキルを練習できる
  • 進捗状況を追跡して上司と共有できる
  • メンターやキャリアチャンスと繋がることができる