JavaScript で DOM データ バインディングを実装する

Tahseen Tauseef 2023年10月12日
  1. JavaScript での一方向データ バインディング
  2. JavaScript での限定的な双方向データ バインディング
  3. JavaScript での双方向データ バインディングの改善
JavaScript で DOM データ バインディングを実装する

足し算が数学に重要であるのと同様に、データバインディングは現在のアプリケーションにとって重要です。 それなしで重要なアプリケーションを作成するのは非常識に思えます。

データ バインディングに関連する Model-View-Controller パターンは、何百もの子孫を生み出してきた最も基本的なアーキテクチャ パターンの 1つです。

この記事では、JavaScript でのデータ バインディングについて説明し、さまざまなメソッドを共有します。

JavaScript での一方向データ バインディング

存在する JavaScript フレームワークの動機の多くは、JavaScript の認識された欠点から生じています。 ただし、言語としての JavaScript は、フレームワークの必要性が大幅に減少するところまで成長しました。

データ バインディングの概念は非常に基本的なものです。 一方にはデータ モデルがあり、もう一方にはビューと呼ばれることが多いインターフェイスがあります。

目的は、一部のデータをビュー上の何かにバインドして、データが変更されるとビューも変更されるようにすることです。 これは、読み取り専用データによく見られます。

モデルとビューが変更された場合にのみ、一方向のデータ バインディングが発生します。

最も単純なように見えますが、プロパティの JavaScript ゲッターとセッターにフックする必要があるため、一方向のデータ バインディングは達成するのが最も難しい可能性があります。 Object.defineProperty は、JavaScript で長い間使用されてきました。

このメソッドを使用すると、開発者はオブジェクトのカスタム getter および setter を構築し、既存のものを置き換えることができます。 たとえば、この関数は次のコードで使用され、以前に指定されたプロパティのゲッターとセッターを変更します。

function Binding(b) {
  _this = this this.element = b.element this.value =
      b.object[b.property] this.attribute = b.attribute this.valueGetter =
          function() {
        return _this.value;
      } this.valueSetter =
              function(val) {
        _this.value = val
        _this.element[_this.attribute] = val
      }

              Object.defineProperty(
                  b.object, b.property,
                  {get: this.valueGetter, set: this.valueSetter});
  b.object[b.property] = this.value;

  this.element[this.attribute] = this.value
}

コードを見てみましょう。シャドウ プロパティを作成して値を Binding オブジェクトに格納し、defineProperty を使用してプロパティのゲッターとセッターを設定します。 等号 (=) を使用してプロパティが設定されるたびに、setter メソッドが呼び出されるようになりました。

最後に、関数はプロパティと DOM 要素を指定された値に設定します。 この リンク から、このコードの動作を確認できます。

JavaScript での限定的な双方向データ バインディング

ビューまたはモデルが変更されると、双方向のデータ バインディングが発生します。 双方向バインディングは、同じ基本コードを使用して構築できます。

バインドには、DOM フィードバックを受け取るためにリッスンするイベントが必要です。 関数 addEventListener が呼び出される場所に注意してください。

これにより、渡された要素にイベント リスナーが追加されます。 イベント ハンドラーは、イベントが呼び出されるたびに、要素にデータ バインドされたオブジェクト値のシャドウ コピーを設定します。

モデルを変更すると、DOM 要素も更新されます。

function Binding(b) {
  _this = this this.element = b.element this.value =
      b.object[b.property] this.attribute = b.attribute this.valueGetter =
          function() {
        return _this.value;
      } this.valueSetter =
              function(val) {
        _this.value = val
        _this.element[_this.attribute] = val
      }

  if (b.event) {
    this.element.addEventListener(b.event, function(event) {
      _this.value = _this.element[_this.attribute]
    })
  }

  Object.defineProperty(
      b.object, b.property, {get: this.valueGetter, set: this.valueSetter});
  b.object[b.property] = this.value;

  this.element[this.attribute] = this.value
}

この リンク から、このコードの動作を確認できます。

ただし、このコードは、要素と属性に対して 1つの一方向または双方向のデータ バインディングのみを許可するという点で、比較的制限的です。

JavaScript での双方向データ バインディングの改善

プロパティを 1つ以上の項目に関連付けることができるようにすることは、双方向のデータ バインディングへの優れたアプローチです。 これは、値が変更された場合 (DOM イベントが呼び出された場合やモデルが変更された場合)、データ バインディングが DOM 上の多数の項目を更新できることを意味します。

また、新しく導入された addBinding メソッドにも注目してください。 これにより、イベントとのバインディングに新しいコンポーネントを追加できるようになりました。

これらは、配列 elementBindings に追加されます。 セッター メソッドは、新しい値が設定されてプロパティが変更されると、elementBindings 配列全体を反復処理します。

また、addBinding の実行時に event オプションを省略して、一方向のデータ バインディングを作成するために使用することもできます。

function Binding(b) {
  _this = this this.elementBindings = [] this.value =
      b.object[b.property] this.valueGetter =
          function() {
        return _this.value;
      } this.valueSetter =
              function(val) {
        _this.value = val
        for (var i = 0; i < _this.elementBindings.length; i++) {
          var binding = _this.elementBindings[i]
          binding.element[binding.attribute] = val
        }
      } this.addBinding =
                  function(element, attribute, event) {
        var binding = { element: element, attribute: attribute } if (event) {
          element.addEventListener(event, function(event) {
            _this.valueSetter(element[attribute]);
          })
          binding.event = event
        }
        this.elementBindings.push(binding)
        element[attribute] = _this.value
        return _this
      }

                  Object.defineProperty(
                      b.object, b.property,
                      {get: this.valueGetter, set: this.valueSetter});

  b.object[b.property] = this.value;
}

この リンク から、このコードの動作を確認できます。

この実装の方が優れていますが、まだやるべきことがたくさんあります。 たとえば、データの検証、テンプレート化、配列のサポート、カスタム UI セッターまたは UI フォーマッター、およびその他の多くの制限はありません。

これらすべてを組み合わせると、フレームワークという用語が形成され始め、それらの多くが存在します。 ただし、このような単純なスクリプトは、多くのニーズに対応するデータ バインディングに必要なものを十分に処理できます。

関連記事 - JavaScript DOM