Reputation: 3360
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?
Upvotes: 6
Views: 11310
Reputation: 13549
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!
Upvotes: 9
Reputation: 206
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#btnContainer
tag to the first ancestor of the<button>
that is not part of such a logical block.
Upvotes: 1
Reputation: 451
In my situation I was working with a observableArray that need some jQuery magic applied to each item in that array. I was having some issues with the custom binding not wanting to take two varibles from each item in that array. So, in my packaging I added some of that dark mnagic to an non observable item as an function so that when the name was cliked, then the map I was working on would move to that location.
HTML:
<ul data-bind="foreach: filteredItems">
<li class="seat">
<span class="name" data-bind="text:fullName, click: toggleEmp"></span>
</li>
</ul>
CODE:
function viewModel() {
var vm = this;
vm.empData = function(data) {
var emp = this;
emp.office_x_loc = ko.observable(data.xLoc);
emp.office_y_loc = ko.observable(data.yLoc);
emp.toggleEmp = function () {
jQuery('#skyLineMap').lhpMegaImgViewer( 'setPosition', emp.office_x_loc(),emp.office_y_loc(), 0.8, false );
};//*/
}//*/
I hope this helps
Upvotes: 0
Reputation: 5412
The note you refer to for the click binding refers to method in your viewmodel, not to your custom click handler.
In other words, if you have a data-bind="click: doSomething"
in the <button>
element, the doSomething()
method in your viewmodel should return true if you want it to execute your custom handler.
Upvotes: -1