進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

ブラウザの 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 コンポーネントフレームワーク: Aura コンポーネントおよび Lightning Web コンポーネント

Spring '19 リリース (API バージョン 45.0) では、Lightning Web コンポーネントモデルと従来の Aura コンポーネントモデルの 2 つのプログラミングモデルを使用して Lightning コンポーネントを作成できます。Lightning Web コンポーネントは、HTML と最新の JavaScript を使用して作成されるカスタム HTML 要素です。Lightning Web コンポーネントと Aura コンポーネントは、ページ上に共存可能で、同時に使用できます。このコンテンツでは、Lightning Web コンポーネントについてのみ説明します。

Lightning Web コンポーネントと DOM

メモ

メモ

これ以降の各単元には、Lightning Web コンポーネントとの結び付きを具体的に説明するいくつかの例を示す同様のセクションがあります。ここで学習した内容を適用したい場合は、Trailhead の「Lightning Web Components Basics (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 属性の text プロパティが JavaScript の text プロパティにバインドされ、UI にデータが表示されます。 

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

以下はそのコードです。

// JavaScript Module demo2.js
import { LightningElement, track } from 'lwc';
export default class Demo2 extends LightningElement {
    @track 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>

text プロパティをもう一度見ると、一定の新しい動作が追加されていることがわかります。@track を使用して、JavaScript の比較的新しいデコレータという機能を活用しています。このデコレータは、値が変更されたときに、コンポーネントの DOM が再表示されるようにする機能をプロパティに追加します。

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

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

明示的な DOM 操作

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

条件付き表示

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

//conditionalButton.js
import { LightningElement, track } from 'lwc';
export default class ConditionalButton extends LightningElement {
    @track 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 if:true={show}>
                    Peek-a-boo!
                </template>
                <template if:false={show}>
                    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 コンポーネントで通常どおりに機能しますが、この特定のモジュールの範疇外です。 

リソース

Trailhead モジュール: Lightning Web Components Basics (Lightning Web コンポーネントの基本)

Lightning Web Components Developer Guide (Lightning Web コンポーネント開発者ガイド): HTML Templates (HTML テンプレート)

Lightning Web Components Developer Guide (Lightning Web コンポーネント開発者ガイド): HTML Template Directives (HTML テンプレートのディレクティブ)

Lightning Web Components Developer Guide (Lightning Web コンポーネント開発者ガイド): Render DOM Elements Conditionally (DOM 要素の条件付き表示)

Lightning Web Components Developer Guide (Lightning Web コンポーネント開発者ガイド): DOM Access Containment (DOM のアクセスコンテインメント)

Lightning Web Components Developer Guide (Lightning Web コンポーネント開発者ガイド): Shadow DOM

UI フレームワーク:

Third party Web API (サードパーティの Web API)