1

In my knockout app I want to be able to globally detect onkeyup/onkeydown events and run testMethod when that happens. What I have tried so far is a custom bindinghandler

ko.bindingHandlers.returnKey = {
  init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
    ko.utils.registerEventHandler(element, 'keydown', function (evt) {
      if (evt.keyCode === 13) {
        evt.preventDefault();
        evt.target.blur();
        valueAccessor().call(viewModel);
      }
    });
  }
};

This however is only working for input elements like <input data-bind="returnKey: testMethod" />

What I would want to do is data-bind to body tag or similar. How can I achieve this?

HischT
  • 933
  • 1
  • 9
  • 26
  • on `body` tag well thats not possible (IMO) . keyup,keydown events works for input elements where actually you key something in . cheers – super cool Jun 11 '15 at 09:52

2 Answers2

2

You haven't specified which browsers you need to support, but you could consider using the event capturing phase to do this (supported >= IE9). Here's a version that skips Knockout, since you want to do it globally (on body) anyways:

var vm = {
  obs1: ko.observable("my text"),
  obs2: ko.observable("my text"),
  obs3: ko.observable(false)
};

function testMethod(eventArgs) {
  console.log(eventArgs.keyCode);
  if (eventArgs.keyCode === 13) {
    eventArgs.preventDefault();
  }
}

document.body.addEventListener('keydown', testMethod, true);

ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<textarea data-bind="value: obs1"></textarea>
<input data-bind="value: obs2" />
<div>Test abc</div>
<input data-bind="checked: obs3" type="radio" />
<hr />
<pre data-bind="text: ko.toJSON($root)"></pre>

Notice that (because of preventDefault) it is now impossible to place a return (newline) inside the textarea, nor tab into the radio button to use "enter" to check it.

If you want, you could also scope attaching the event listener to your binding handler, and use the bound observable inside testMethod.

Community
  • 1
  • 1
Jeroen
  • 60,696
  • 40
  • 206
  • 339
2

Use an event binding on whatever region you want to capture events within. Key events will only happen on elements that can get focus (inputs, buttons and such), when the focus is on them. (This is true if you use addEventListener, too.)

<div data-bind="event:{keydown:testMethod}">

The test method will prevent default behavior if it does not return true.

testMethod: function (element, eventArgs) {
    vm.pressCount(vm.pressCount() + 1);
    var prevent = eventArgs.keyCode === 13;
    return !prevent;
}

Demo: http://jsfiddle.net/tmL3chb6/

Roy J
  • 42,522
  • 10
  • 78
  • 102