How can I disable and enable an anchor tag with this custom binding. It works great with input elements but the anchor tag just changes the CSS, not the disabling.
<a href="link" data-bind="myDisabled: !enabled()"/>
ko.bindingHandlers.myDisabled = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.css.update(element, function() {return { disabled: value }; });
ko.bindingHandlers.disable.update(element, valueAccessor);
}
};
How can I disable and enable an anchor tag with this custom binding. It works great with input elements but the anchor tag just changes the CSS, not the disabling.
<a href="link" data-bind="myDisabled: !enabled()"/>
ko.bindingHandlers.myDisabled = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.css.update(element, function() {return { disabled: value }; });
ko.bindingHandlers.disable.update(element, valueAccessor);
}
};
Share
Improve this question
asked May 30, 2012 at 16:12
Mike FlynnMike Flynn
24.3k59 gold badges194 silver badges367 bronze badges
3
- I've only seen anchor tags "disabled" by setting their onlick to false. What is it you mean want to happen to the anchor to "disable" it? – Kyeotic Commented May 30, 2012 at 16:44
- Just to make sure it doesnt fire any click events, and when enabled, the click events are renabled. – Mike Flynn Commented May 30, 2012 at 17:19
- 1 As an alternative you can use KOs comment logic to just produce a different tag. See: stackoverflow.com/q/15969045/52551 – Spencer Ruport Commented Dec 23, 2013 at 17:40
4 Answers
Reset to default 11You need to capture click event in your binding handler.
HTML:
<a href="link" data-bind="disableClick: !enabled()">test</a>
<br/><br/><br/>
<input type="checkbox" data-bind="checked: enabled"> enabled
JavaScript:
ko.bindingHandlers.disableClick = {
init: function (element, valueAccessor) {
$(element).click(function(evt) {
if(valueAccessor())
evt.preventDefault();
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.css.update(element, function() {return { disabled_anchor: value }; });
}
};
ko.applyBindings({ enabled: ko.observable(false)});
Here is a working example:
http://jsfiddle.net/kp74u/54/
UPDATE 1: If you need to prevent other event handlers bound after knockout binding handler was attached, you need to add stopImmediatePropagation
to the event handler along with preventDefault
.
example: http://jsfiddle.net/kp74u/55/
UPDATE 2: If you want to disable all event handlers (along with click event handlers attached before your binding handler, you need to 'hack' the jquery events array). Please note that this may not work other versions of jquery (example uses 1.7):
ko.bindingHandlers.disableClick = {
init: function(element, valueAccessor) {
$(element).click(function(evt) {
alert('test before');
});
$(element).click(function(evt) {
if (valueAccessor()) {
evt.preventDefault();
evt.stopImmediatePropagation();
}
});
//begin of 'hack' to move our 'disable' event handler to top of the stack
var events = $.data(element, "events");
console.log(events);
var handlers = events['click'];
if (handlers.length == 1) {
return;
}
handlers.splice(0, 0, handlers.pop());
//end of 'hack' to move our 'disable' event handler to top of the stack
$(element).click(function(evt) {
alert('test after');
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.css.update(element, function() {
return {
disabled_anchor: value
};
});
}
};
example: http://jsfiddle.net/nickolsky/kp74u/40/
UPDATE 3: As was mentioned there (suggested edit by FIR55TORM, sorry can't approve this completely correct edit because I am too late to review): if you're using jQuery 1.10.x, you will need to add an underscore to access the 'data' object like so:
var events = $._data(element, "events");
Revised fiddle for jQuery 1.10.x: http://jsfiddle.net/nickolsky/kp74u/41/
I found this answer when goggling for a way of doing this, but I didn't like the approach so did my own
var orgClickInit = ko.bindingHandlers.click.init;
ko.bindingHandlers.click.init = function (element, valueAccessor, allBindingsAccessor, viewModel) {
if (element.tagName === "A" && allBindingsAccessor().enable != null) {
var disabled = ko.computed(function () {
return ko.utils.unwrapObservable(allBindingsAccessor().enable) === false;
});
ko.applyBindingsToNode(element, { css: { disabled: disabled} });
var handler = valueAccessor();
valueAccessor = function () {
return function () {
if (ko.utils.unwrapObservable(allBindingsAccessor().enable)) {
handler.apply(this, arguments);
}
}
};
}
orgClickInit(element, valueAccessor, allBindingsAccessor, viewModel);
};
Its seamless with the native click and enable binding (disable binding not implemented) Fiddle (Fiddle also uses my Convention over configuration library) http://jsfiddle.net/xCfQC/30/
Using @Anders answer as inspiration, I came up with my own version of this. Allows for use of "enable", "disable" with or without the "click". Also allows for a custom disabled class, otherwise defaults to "disabled".
var koEnableUpdateOrig = ko.bindingHandlers.enable.update;
ko.bindingHandlers.enable.update = function (element, valueAccessor, allBindings) {
// call original enable update
var result = koEnableUpdateOrig.apply(this, arguments);
var enabled = ko.unwrap(valueAccessor());
// get and apply disabled class
var disabledClass = "disabled";
if (allBindings)
disabledClass = allBindings().disabledClass || "disabled";
if (enabled) {
$(element).removeClass(disabledClass);
if (element.tagName === "A")
$(element).off("click.koEnableUpdate");
}
else {
$(element).addClass(disabledClass);
if (element.tagName === "A")
$(element).on("click.koEnableUpdate", function (e) { e.preventDefault(); });
}
return result;
};
ko.bindingHandlers.disable.update = function (element, valueAccessor, allBindings) {
// call enable with the reverse value
// the original knockout disable does this, but does not pass the allBindings
ko.bindingHandlers.enable.update(element, function () {
return !ko.unwrap(valueAccessor())
}, allBindings);
};
var koClickInitOrig = ko.bindingHandlers.click.init;
ko.bindingHandlers.click.init = function (element, valueAccessor, allBindings) {
// wrap click function with enable/disable check
var valueAccessorOrig = valueAccessor();
valueAccessor = function () {
return function () {
if (ko.unwrap(allBindings().enable) ||
(allBindings().disable == null || !ko.unwrap(allBindings().disable))) {
valueAccessorOrig.apply(this, arguments);
}
}
};
// apply wrapped click to original click init
koClickInitOrig.apply(this, arguments);
};
This is my approach:
JavaScript
(function () {
var originalDisableUpdate = ko.bindingHandlers.disable.update;
ko.bindingHandlers.disable.update = function (element, valueAccessor) {
if (element.tagName === 'A') {
var
value = ko.utils.unwrapObservable(valueAccessor()),
disabled = 'disabled';
if (value) {
element.setAttribute(disabled, null);
}
else {
element.removeAttribute(disabled);
}
}
else {
originalDisableUpdate(element, valueAccessor);
}
};
})();
CSS
a[disabled] {
pointer-events:none;
cursor:default;
}