0

I'm using KnockoutJS and I noticed a problem with bindings that I can explain using this example:

<label><input type="checkbox" data-bind="checked: displayMessage" /> Display message</label>
<div data-bind="if: displayMessage">Here is a message.<span id="super">SUPER</span> Astonishing.</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
$(function(){
    ko.applyBindings({
        displayMessage: ko.observable(true)
    });

    $('#super').on("click", function(){
        alert('SUUUUUPER');
    });
});

When you first click the "SUPER" span element the alert shows up. However, if you remove the block by setting the property displayMessage of the model to false and putting it back to true to display it again the binding to the click event is no longer working.

From the source code of if, ifnot, and with bindingHandlers I know that knockout removes the DOM clone and saves it the first time and re appends it using a virtual element API.

So my question is: knowing that the jQuery on() utility attaches an event handler to the selected element even if they don't exist yet. Is the use of virtual element who's causing the click binding to be lost. If not: explain to me what happens.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129

1 Answers1

1

I'd stay away from using id on elements withing KO-controlled markup. The thing is: Knockout will clone elements, restructure the DOM, etc, and you may end up with multiple elements with the same id, which is invalid and from which point browser behavior based off IDs is unpredictable.

In addition, because with if bindings KO will remove/add DOM nodes on the fly, you'd need this advice to make sure the jQuery on function handles freshly created elements too.

Here's a different version using class instead of id that does work as expected:

$(function(){
    ko.applyBindings({
        displayMessage: ko.observable(true)
    });

    $(document.body).on("click", ".super", function(){
        alert('SUUUUUPER');
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

<label><input type="checkbox" data-bind="checked: displayMessage" /> Display message</label>
<div data-bind="if: displayMessage">Here is a message.<span class="super">SUPER</span> Astonishing.</div>

Note that:

  • You could use the visible binding too, which may prevent the need for mentioned on-advice.
  • You should try to prevent using jQuery for event handling like that, and use Knockout's MVVM-style event handling instead (utilize the click binding).
Community
  • 1
  • 1
Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • Thank you for your response ... in my personal project i try to use knockout to bind click as you said the problem is that in some UI i use external plugins (like datetimepicker which is a jquery based plugin) that bind eventhandlers in their implementation and those are unfortunately lost. – user3123258 Jun 25 '15 at 09:59
  • 1
    @user3123258 All DOM manipulation in Knockout should happen in bindings. That means if you use plugins they should have custom binding handlers to set them up. See http://stackoverflow.com/questions/6612705/jquery-ui-datepicker-change-event-not-caught-by-knockoutjs for example. – Roy J Jun 25 '15 at 10:12
  • THHX Roy J ... i wasnt aware of that ... using ko custom binding to use the Plugin API is a Good Idea ^^ – user3123258 Jun 25 '15 at 10:21