admin管理员组

文章数量:1022443

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?

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?

Share Improve this question asked Mar 10, 2015 at 3:49 marked-downmarked-down 10.4k22 gold badges93 silver badges153 bronze badges 4
  • Where are you using this template(#video-file-template)?? – Madhu Commented Mar 10, 2015 at 4:05
  • Template is not evaluated as DOM element. I think you didn't bind your template using knockout before querying the DOM. – Nhân Nguyễn Commented Mar 10, 2015 at 5:30
  • Yes, defenitely you're trying to execute you jquery statement before KO bindings are applied. Check the order of script execution. – Rango Commented Mar 10, 2015 at 8:58
  • The next question would of course be why you are trying to get the element using jQuery in a $(document).ready callback. I don't know much about your specific scenario, but my guess would be that you'd be a lot better off creating a custom bindingHandler which performs the logic you want. – Robert Westerlund Commented Mar 10, 2015 at 9:34
Add a ment  | 

2 Answers 2

Reset to default 5

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 acplish 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./knockout/knockout/tree/master/src/binding/defaultBindings

As the previous ments 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 pleted;

So:

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

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?

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?

Share Improve this question asked Mar 10, 2015 at 3:49 marked-downmarked-down 10.4k22 gold badges93 silver badges153 bronze badges 4
  • Where are you using this template(#video-file-template)?? – Madhu Commented Mar 10, 2015 at 4:05
  • Template is not evaluated as DOM element. I think you didn't bind your template using knockout before querying the DOM. – Nhân Nguyễn Commented Mar 10, 2015 at 5:30
  • Yes, defenitely you're trying to execute you jquery statement before KO bindings are applied. Check the order of script execution. – Rango Commented Mar 10, 2015 at 8:58
  • The next question would of course be why you are trying to get the element using jQuery in a $(document).ready callback. I don't know much about your specific scenario, but my guess would be that you'd be a lot better off creating a custom bindingHandler which performs the logic you want. – Robert Westerlund Commented Mar 10, 2015 at 9:34
Add a ment  | 

2 Answers 2

Reset to default 5

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 acplish 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./knockout/knockout/tree/master/src/binding/defaultBindings

As the previous ments 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 pleted;

So:

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

本文标签: javascriptHow to use jQuery in a Knockoutjs TemplateStack Overflow