この質問は教育目的としてのみ扱ってください。私はまだこれを実装するための新しい回答やアイデアを聞くことに興味があります。
要約
JavaScript を使用して双方向データバインディングを実装するにはどうすればよいですか?
DOMへのデータバインディング
DOM へのデータ バインディングとは、たとえば、a
プロパティを持つJavaScript オブジェクトを持つことを意味しますb
。次に、<input>
DOM 要素 (たとえば) を持つと、DOM 要素が変更されると、DOM 要素a
も変更され、その逆も同様になります (つまり、双方向データ バインディングを意味します)。
AngularJS の図でこれがどのように見えるかを示します。
つまり、基本的には次のような JavaScript になります。
var a = {b:3};
次に、次のような入力 (またはその他のフォーム) 要素を作成します。
<input type='text' value=''>
入力の値を の値a.b
(たとえば) にして、入力テキストが変更されたら、入力もa.b
変更したいと思います。JavaScripta.b
で が変更されると、入力も変更されます。
質問
これをプレーンな JavaScript で実現するための基本的なテクニックは何ですか?
具体的には、以下を参考にして良い回答をいただきたいです。
- オブジェクトのバインディングはどのように機能しますか?
- フォームの変化を聴くことはどのように機能するでしょうか?
- テンプレート レベルで HTML のみを変更する簡単な方法は可能ですか? HTML ドキュメント自体のバインディングを追跡するのではなく、JavaScript (DOM イベントと、使用されている DOM 要素への参照を保持する JavaScript) のみで追跡したいと思います。
何を試しましたか?
私は Mustache の大ファンなので、テンプレート作成に使用してみました。しかし、Mustache は HTML を文字列として処理するため、データ バインディング自体を実行しようとしたときに問題が発生しました。そのため、結果を取得した後、ビューモデル内のオブジェクトがどこにあるか参照できません。この問題に対する唯一の回避策は、属性を使用して HTML 文字列 (または作成された DOM ツリー) 自体を変更することでした。別のテンプレート エンジンを使用してもかまいません。
基本的に、私は目の前の問題を複雑にしているという強い思いを抱き、そこには簡単な解決策があると考えました。
注:外部ライブラリ、特に数千行のコードを使用する回答はしないでください。私は AngularJS と KnockoutJS を使用しました (そして気に入っています)。「フレームワーク x を使用する」という形式の回答は絶対に望んでいません。理想的には、多くのフレームワークの使い方を知らない将来の読者が、双方向データ バインディングを自分で実装する方法を理解できるようにしたいと思っています。完全な回答ではなく、アイデアが伝わる回答を期待しています。
ベストアンサー1
- オブジェクトのバインディングはどのように機能しますか?
- フォームの変化を聴くことはどのように機能するでしょうか?
両方のオブジェクトを更新する抽象化
他にもテクニックはあると思いますが、最終的には、関連する DOM 要素への参照を保持し、独自のデータと関連要素への更新を調整するインターフェイスを提供するオブジェクトが必要になります。
は、.addEventListener()
このための非常に優れたインターフェースを提供します。インターフェースを実装するオブジェクトを与えるeventListener
と、そのオブジェクトを値としてハンドラーが呼び出されますthis
。
これにより、要素とその関連データの両方に自動的にアクセスできるようになります。
オブジェクトの定義
プロトタイプ継承は、これを実装する良い方法ですが、もちろん必須ではありません。まず、要素と初期データを受け取るコンストラクターを作成します。
function MyCtor(element, data) {
this.data = data;
this.element = element;
element.value = data;
element.addEventListener("change", this, false);
}
ここで、コンストラクターは新しいオブジェクトのプロパティに要素とデータを格納します。また、change
指定された にイベントをバインドしますelement
。興味深いのは、2 番目の引数として関数ではなく新しいオブジェクトを渡すことです。ただし、これだけでは機能しません。
eventListener
インターフェースの実装
これを機能させるには、オブジェクトにeventListener
インターフェースを実装する必要があります。これを実現するために必要なのは、オブジェクトにhandleEvent()
メソッドを与えることだけです。
ここで継承が重要になります。
MyCtor.prototype.handleEvent = function(event) {
switch (event.type) {
case "change": this.change(this.element.value);
}
};
MyCtor.prototype.change = function(value) {
this.data = value;
this.element.value = value;
};
これを構造化する方法はたくさんありますが、更新を調整する例では、change()
メソッドが値のみを受け入れ、handleEvent
イベント オブジェクトの代わりにその値を渡すようにすることにしました。この方法では、change()
イベントがなくても呼び出すことができます。
これで、change
イベントが発生すると、要素とプロパティの両方が更新されます。JavaScriptプログラムを.data
呼び出す場合も同じことが起こります。.change()
コードの使用
これで、新しいオブジェクトを作成し、更新を実行できるようになります。JS コードの更新は入力に表示され、入力の変更イベントは JS コードに表示されます。
var obj = new MyCtor(document.getElementById("foo"), "20");
// simulate some JS based changes.
var i = 0;
setInterval(function() {
obj.change(parseInt(obj.element.value) + ++i);
}, 3000);