Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery click event not firing when using knockout

According to knockout click binding documentation's "Note 3", Knockout prevents click event from performing default function. To override this behavior, all I have to do is return true from my handler function. So, I have this markup:

<div data-bind="visible:Pages().length == 0">
    <div class="alert alert-info">Click "Parse" button.</div>
    <button class="btn" id="btnParse" title="Parse Tabs">Parse</button>
</div>

Now, I want to attach a click event to the button like this:

$(function () {       
    $('#btnParse').on('click', function () { alert('clicked'); return true;});
});

Note that I'm returning true from the function. This even handler never fires. How can I make this work?

like image 510
Alex Polkhovsky Avatar asked May 08 '12 20:05

Alex Polkhovsky


2 Answers

The reason your click handler is never firing is because it's never applied to your element. So when you do this in jquery:

$('.some-class').on('some-event', someFunction);

Then for the handler to be bound to that event, first jQuery has to find your $('.some-class') selector. In your case, most likely #btnParse is not yet rendered to the page by knockout when you bind the event. Or, also possible, that original element is rendered, destroyed, and then another element is rendered. In this second scenario, the event handler wouldn't remain on the button. One alternative (which I don't recommend) is to bind the handler higher up in the DOM, like at the document level, and filter events to only those of something with an id #btnParse:

$(document).on('click', '#btnParse', function () { console.log('hi'); });

The reason I don't recommend that is because it's bad knockout practice, you should be using the click binding as some other posts suggested. Also, you're using an id attribute and that's really not a good idea in general for templated dynamic content - just use classes unless you absolutely need an id for a unique static element.

As for how to properly use knockout's click binding, the one tricky thing is that you'll need to understand how knockout does scoping. If, for example, you're binding a click inside a loop, and you want the handler from your main view model, you have to reference the parent scope because the loop changes your context:

<!-- ko foreach: someCollection -->
    <a data-bind="click: $parent.someFunction"></a>
<!-- /ko -->

Furthermore, if you need to change the Javascript context that your handler executes with (the this), then you need to bind the click handler like this:

<!-- ko foreach: someCollection -->
    <a data-bind="click: $parent.someFunction.bind($parent)"></a>
<!-- /ko -->

Play with that stuff a bit and ask a new question if you're still confused. Good luck!

like image 159
Milimetric Avatar answered Sep 19 '22 20:09

Milimetric


I think the problem has to do with knockout's modification of the DOM where you are trying to bind the event. To get around this, try assigning the click function to the parent<div>element. When assigning the click handler, use the overload with a selector to specify that you want to handle the click only when the contained button is clicked. For example, change div like this:

<div id="btnContainer" data-bind="visible:Pages().length == 0">
    <div class="alert alert-info">Click "Parse" button.</div>
    <button class="btn" id="btnParse" title="Parse Tabs">Parse</button>
</div>

And then change your script to:

$(function () {       
    $('#btnContainer').on('click', '#btnParse', function () { alert('clicked');});
});

Note that if this<div>is contained within another logical block that modifies the DOM, (a template, foreach loop, etc), you'll probably need to set the#btnContainertag to the first ancestor of the<button>that is not part of such a logical block.

like image 22
Flangus Avatar answered Sep 18 '22 20:09

Flangus