web-dev-qa-db-ja.com

Knockout.jsはデータに関連付けられたdomオブジェクトを取得します

私はknockout.jsと協力して動的リストを構築し、観察可能な配列内のオブジェクトに関連付けられたDOMオブジェクトを取得する方法を見つけようとしています。具体的には、行のjQueryを取得します。

例:

<ul data-bind="foreach: Item">
    <li data-bind="events: {click: getDomObject}, text: 'text: ' + text">
    </li>
</ul>

getDomObject関数では、特定の<li></li> DOMオブジェクトを取得できるようにして、jQuery操作を行えるようにします。

Item ViewModelにidメンバーを追加し、そのIDを広告申込情報のhtml idとして追加し、それに基づいて選択することを考えましたが、もっと簡単な方法があるはずです。

Knockout.jsによって生成されたダイナミックHTMLを参照する適切な方法は何ですか?

48
joe_coolish

クリックなどのイベントハンドラーには2つの引数が渡されます。あれは

  1. このイベントが属するアイテム-foreachバインディングでレンダリングしている観察可能な配列のエントリのように(あなたの場合は「アイテム」)。

  2. そして、実際のイベントに関する詳細情報を提供するイベントオブジェクト。このオブジェクトには、クリックされたDOM要素(キー「ターゲット」)が含まれます。

    getDomObject = function(item, event) {
        var $this = $(event.target);
        // ...
    }
    

注:ノックアウトとネイティブjQuery DOM操作を混在させないでください-巧妙なノックアウトバインディングで同じ結果を達成できる場合は、それを使用することをお勧めします。

そして、ここに簡単なデモがあります: http://jsfiddle.net/KLK9Z/213/

var Item = function(color) {
  this.color = String(color);
  this.setTextColor = function(item, event) {
    $(event.target).css('background', color);
  };
};

ko.applyBindings(new function() {
  this.Items = ko.observableArray([
    new Item('red'),
    new Item('blue'),
    new Item('green')
  ]);
}());
li {
  padding: 2px 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.0.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<ul data-bind="foreach: Items">
  <li>
    <button data-bind="click: setTextColor, text: 'Color: ' + color"></button>
  </li>
</ul>
66
Niko

$(event.target)ソリューションは、アイテムのDOM要素がターゲットにある既に発生しているイベントに関連している場合に適しています。ただし、イベントがないため、対象のアイテムがない場合があります(たとえば、ユーザーが操作していないアイテムにリストをスクロールする場合)。

そのような場合、アイテムのDOM要素のid属性に、アイテムIDを含む一意の値を与えることができます。

<li data-bind="attr: {id: 'item_' + id}">

getDomObject()は次のようになります。

getDomObject = function(item) { return $("#item_" + item.id); }
20
yuvalr80

3番目のオプションを追加するために、作業するイベントがない場合にも(イベントがある場合は、受け入れられる回答が最適/最適化されます)。

次のようなカスタムバインディングを作成します。

ko.bindingHandlers.scrollTo = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value) {
            var scrollParent = $(element).closest("div");
            var newTop = $(element).position().top + scrollParent.scrollTop();
            scrollParent.scrollTop(newTop);
        }
    }
};

使用法は次のとおりです。

<li data-bind="scrollTo: $parent.scrollTo() && $parent.scrollTo().id == id">

上記の場合、$ parentは私のビューモデルです。一意のIDを含む監視可能なオブジェクトがあります。 scrollTo()オブジェクトを設定するたびに、リストはそのアイテムにスクロールします。

私のコードでは、LIの親DIVにスクロールバー(overflow:auto/scroll)があると想定していることに注意してください。必要に応じて調整し、親のクラスを使用してjQueryセレクターに使用するか、データバインドオプションを介してセレクターを渡すことができます。私にとっては、これで十分です。スクロール可能なセクションには常にdivを使用します。

7
eselk

同様の問題がありました。 Backbone.jsのelおよび$ el参照の使用に似たソリューションを考え出します。

あなたのViewModelで:

var myViewModel = function(){
  var self = this;

  //html element
  self.el = ko.observable();

  //jquery wrapped version
  self.$el = ko.observable();
}

html(リスト要素など):

<!-- left side is the name of the handler, right side is name of the observable -->
<li class="myclass" data-bind="el: el, $el: $el"></li>

bindingHandlers(initに可能なすべての引数を表示):

ko.bindingHandlers.el = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var value = valueAccessor();
    //assign value to observable (we specified in html)
    value(element);
  }
};

ko.bindingHandlers.$el = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var value = valueAccessor();
    //here we first create a jQuery object by using $(myelem)
    //before updating observable value
    value($(element).first());
  }
};

たとえば、次のように$ elを使用できます。

var myViewModel = function(){
  var self = this;

  //plain DOM element reference
  self.el = ko.observable();

  //jquery object reference
  self.$el = ko.observable();

  self.myFunction = function() {
    console.log(self.$el().html());
    self.$el().addClass("myCssClass");
  }
}

お役に立てれば!

5
Murat Ozgul

私の解決策(「値」バインディングに有効)

 ko.bindingHandlers.value.preprocess = function(val, name, cb) {
    /* every time I set a data-bind="value: xxxx" with an 
     * observable xxxx add also a data-bind="domElement: xxxx" */
    cb('domElement', val );
    return val;
}

ko.bindingHandlers.domElement = {
    /* For each data-bind="domElement: xxxx" add an extension "element" */
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
      valueAccessor().extend({element: element });
    }
  };

ko.extenders.element = function (target, element) {
    /* element extension add el and $el to observable xxxx */
    target.el = element;
    target.$el = $(element);
} 

これで、jqueryおよびDOM要素にバインドするyourobservable。$ elとyourobservable.elができました。

4
Emiliano Bruni