How to use jQuery in a Knockout.js Template?

2019-07-20 20:50发布

问题:

I'm trying to target an element using jQuery which is embedded in one of my knockout templates:

<script type="text/html" id="video-file-template">
    <div class="video" data-bind="attr: { 'data-index': $index }">
    </div>
</script>

Yet, when I attempt to select $('.video') using jQuery, wrapped in a document ready function, I get an object with a length of 0 returned:

$(document).ready(function() {
    console.log($('.video')); // Returns an object with a length of 0
});

Why is this? Is it because the element is not part of the DOM when my jQuery script is evaluated? If so, how can I target the element when it is loaded into the DOM via Knockout.js?

回答1:

It's true that the document is ready before ko.applyBindings finishes, so that's why you're not seeing the element. However, you should not be using jQuery to violate the boundary between your view model and the DOM like that. In knockout, the way to accomplish what you need is with custom bindings.

Basically, you define a new knockout binding (like text, value, foreach, etc) and you have access to an init function, which fires when the element is first rendered, and an update function, which fires when the value you pass to the binding is updated. In your case, you would only need to define init:

ko.bindingHandlers.customVideo = {
    init: function (element) {
        console.log(element, $(element));  // notice you can use jquery here
    }
};

And then you use the binding like this:

<div data-bind="customVideo"></div>

Perhaps it's better to add the "video" class and do other initialization right in the init callback:

ko.bindingHandlers.customVideo = {
    init: function (element) {
        $(element).addClass('video');
    }
};

If this feels a little wonky at first, remember there's a very good reason for the indirection. It keeps your view model separate from the DOM it applies to. So you can change the DOM more freely and you can test the view model more independently. If you waited for ko.applyBindings to finish and called some jQuery stuff after that, you'd have a harder time testing that code. Notice that knockout custom bindings are not "special" in any way, and you can see that the built in bindings are defined exactly the same: https://github.com/knockout/knockout/tree/master/src/binding/defaultBindings



回答2:

As the previous comments have suggested, it's because your $(document).ready fires before your knockout templates have been rendered.

Whenever I need to do this sort of thing I tend to have an 'init' (or whatever) function on my ko view model that I call after applyBindings has completed;

So:

var ViewModel = function(){
     var self=this;
     //blah
     self.init = function(){
         //jquery targeting template elements
     }
}
var vm = new ViewModel();
ko.applyBindings(vm);
vm.init();