Skip to content

Commit

Permalink
hasFocus (mixed case) also supports writing to vm properties
Browse files Browse the repository at this point in the history
  • Loading branch information
mbest committed Apr 8, 2017
1 parent 5f0b9a7 commit 00b03e9
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 99 deletions.
192 changes: 95 additions & 97 deletions spec/defaultBindings/hasfocusBehaviors.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,96 @@
describe('Binding: Hasfocus', function() {
beforeEach(jasmine.prepareTestNode);
beforeEach(function() { waits(1); }); // Workaround for spurious focus-timing-related failures on IE8 (issue #736)

it('Should respond to changes on an observable value by blurring or focusing the element', function() {
var currentState;
var model = { myVal: ko.observable() }
testNode.innerHTML = "<input data-bind='hasfocus: myVal' /><input />";
ko.applyBindings(model, testNode);
ko.utils.registerEventHandler(testNode.childNodes[0], "focusin", function() { currentState = true });
ko.utils.registerEventHandler(testNode.childNodes[0], "focusout", function() { currentState = false });

// When the value becomes true, we focus
model.myVal(true);
expect(currentState).toEqual(true);

// When the value becomes false, we blur
model.myVal(false);
expect(currentState).toEqual(false);
ko.utils.arrayForEach(['hasfocus', 'hasFocus'], function(binding) {
describe('Binding: ' + binding, function() {
beforeEach(jasmine.prepareTestNode);
beforeEach(function() { waits(1); }); // Workaround for spurious focus-timing-related failures on IE8 (issue #736)

it('Should respond to changes on an observable value by blurring or focusing the element', function() {
var currentState;
var model = { myVal: ko.observable() }
testNode.innerHTML = "<input data-bind='" + binding + ": myVal' /><input />";
ko.applyBindings(model, testNode);
ko.utils.registerEventHandler(testNode.childNodes[0], "focusin", function() { currentState = true });
ko.utils.registerEventHandler(testNode.childNodes[0], "focusout", function() { currentState = false });

// When the value becomes true, we focus
model.myVal(true);
expect(currentState).toEqual(true);

// When the value becomes false, we blur
model.myVal(false);
expect(currentState).toEqual(false);
});

it('Should set an observable value to be true on focus and false on blur', function() {
var model = { myVal: ko.observable() }
testNode.innerHTML = "<input data-bind='" + binding + ": myVal' /><input />";
ko.applyBindings(model, testNode);

// Need to raise "focusin" and "focusout" manually, because simply calling ".focus()" and ".blur()"
// in IE doesn't reliably trigger the "focus" and "blur" events synchronously

testNode.childNodes[0].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusin");
expect(model.myVal()).toEqual(true);

// Move the focus elsewhere
testNode.childNodes[1].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusout");
expect(model.myVal()).toEqual(false);

// If the model value becomes true after a blur, we re-focus the element
// (Represents issue #672, where this wasn't working)
var didFocusExpectedElement = false;
ko.utils.registerEventHandler(testNode.childNodes[0], "focusin", function() { didFocusExpectedElement = true });
model.myVal(true);
expect(didFocusExpectedElement).toEqual(true);
});

it('Should set a non-observable value to be true on focus and false on blur', function() {
var model = { myVal: null }
testNode.innerHTML = "<input data-bind='" + binding + ": myVal' /><input />";
ko.applyBindings(model, testNode);

testNode.childNodes[0].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusin");
expect(model.myVal).toEqual(true);

// Move the focus elsewhere
testNode.childNodes[1].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusout");
expect(model.myVal).toEqual(false);
});

it('Should not unnecessarily focus or blur an element that is already focused/blurred', function() {
// This is the closest we can get to representing issue #698 as a spec
var model = { isFocused: ko.observable({}) };
testNode.innerHTML = "<input data-bind='" + binding + ": isFocused' />";
ko.applyBindings(model, testNode);

// The elem is already focused, so changing the model value to a different truthy value
// shouldn't cause any additional focus events
var didFocusAgain = false;
ko.utils.registerEventHandler(testNode.childNodes[0], "focusin", function() { didFocusAgain = true });
model.isFocused.valueHasMutated();
expect(didFocusAgain).toEqual(false);

// Similarly, when the elem is already blurred, changing the model value to a different
// falsey value shouldn't cause any additional blur events
model.isFocused(false);
var didBlurAgain = false;
ko.utils.registerEventHandler(testNode.childNodes[0], "focusout", function() { didBlurAgain = true });
model.isFocused(null);
expect(didBlurAgain).toEqual(false);
});

it('Should not cause unrelated items to lose focus when initialized with false', function () {
// See #1893
testNode.innerHTML = '<input data-bind="' + binding + ': true" value="This should be focused initially" /><input data-bind="' + binding + ': false" value="This should not be focused" />';
ko.applyBindings({}, testNode);

// Can only test for focus in browsers that support it
if ("activeElement" in document) {
expect(document.activeElement).toBe(testNode.childNodes[0]);
}
});
});

it('Should set an observable value to be true on focus and false on blur', function() {
var model = { myVal: ko.observable() }
testNode.innerHTML = "<input data-bind='hasfocus: myVal' /><input />";
ko.applyBindings(model, testNode);

// Need to raise "focusin" and "focusout" manually, because simply calling ".focus()" and ".blur()"
// in IE doesn't reliably trigger the "focus" and "blur" events synchronously

testNode.childNodes[0].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusin");
expect(model.myVal()).toEqual(true);

// Move the focus elsewhere
testNode.childNodes[1].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusout");
expect(model.myVal()).toEqual(false);

// If the model value becomes true after a blur, we re-focus the element
// (Represents issue #672, where this wasn't working)
var didFocusExpectedElement = false;
ko.utils.registerEventHandler(testNode.childNodes[0], "focusin", function() { didFocusExpectedElement = true });
model.myVal(true);
expect(didFocusExpectedElement).toEqual(true);
});

it('Should set a non-observable value to be true on focus and false on blur', function() {
var model = { myVal: null }
testNode.innerHTML = "<input data-bind='hasfocus: myVal' /><input />";
ko.applyBindings(model, testNode);

testNode.childNodes[0].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusin");
expect(model.myVal).toEqual(true);

// Move the focus elsewhere
testNode.childNodes[1].focus();
ko.utils.triggerEvent(testNode.childNodes[0], "focusout");
expect(model.myVal).toEqual(false);
});

it('Should be aliased as hasFocus as well as hasfocus', function() {
expect(ko.bindingHandlers.hasFocus).toEqual(ko.bindingHandlers.hasfocus);
});

it('Should not unnecessarily focus or blur an element that is already focused/blurred', function() {
// This is the closest we can get to representing issue #698 as a spec
var model = { isFocused: ko.observable({}) };
testNode.innerHTML = "<input data-bind='hasfocus: isFocused' />";
ko.applyBindings(model, testNode);

// The elem is already focused, so changing the model value to a different truthy value
// shouldn't cause any additional focus events
var didFocusAgain = false;
ko.utils.registerEventHandler(testNode.childNodes[0], "focusin", function() { didFocusAgain = true });
model.isFocused.valueHasMutated();
expect(didFocusAgain).toEqual(false);

// Similarly, when the elem is already blurred, changing the model value to a different
// falsey value shouldn't cause any additional blur events
model.isFocused(false);
var didBlurAgain = false;
ko.utils.registerEventHandler(testNode.childNodes[0], "focusout", function() { didBlurAgain = true });
model.isFocused(null);
expect(didBlurAgain).toEqual(false);
});

it('Should not cause unrelated items to lose focus when initialized with false', function () {
// See #1893
testNode.innerHTML = '<input data-bind="hasFocus: true" value="This should be focused initially" /><input data-bind="hasFocus: false" value="This should not be focused" />';
ko.applyBindings({}, testNode);

// Can only test for focus in browsers that support it
if ("activeElement" in document) {
expect(document.activeElement).toBe(testNode.childNodes[0]);
}
});
});
});
2 changes: 1 addition & 1 deletion src/binding/defaultBindings/hasfocus.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ ko.bindingHandlers['hasfocus'] = {
ko.expressionRewriting.twoWayBindings['hasfocus'] = true;

ko.bindingHandlers['hasFocus'] = ko.bindingHandlers['hasfocus']; // Make "hasFocus" an alias
ko.expressionRewriting.twoWayBindings['hasFocus'] = true;
ko.expressionRewriting.twoWayBindings['hasFocus'] = 'hasfocus';
3 changes: 2 additions & 1 deletion src/binding/expressionRewriting.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ ko.expressionRewriting = (function () {
if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
// For two-way bindings, provide a write method in case the value
// isn't a writable observable.
propertyAccessorResultStrings.push("'" + key + "':function(_z){" + writableVal + "=_z}");
var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key;
propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + writableVal + "=_z}");
}
}
// Values are wrapped in a function so that each value can be accessed independently
Expand Down

0 comments on commit 00b03e9

Please sign in to comment.