Florin
Florin

Reputation: 43

using hasFocus binding on table or div element

So basically I have this input text box:

 <input id="usersInputField" data-bind="hasFocus: isSearchInputFocused"/>

There is a table which pops whenever the input field has focus:

<div data-bind="visible: isSearchInputFocused">
   <table>
       <tbody data-bind="foreach: usersFromSearch">
           <!-- some info -->
       </tbody>
   </table>
</div>

...which works great. Although, I want the table to stay visible in case the user clicks on the div also.

Instead of the isSearchInputFocused observable in the table binding, I tried to use a ko.computed observable which returned true if the text box or the user table was focused and added a hasFocus binding on it, but that doesn't seem to work. I assume the hasFocus binding has priority over the click binding.

How can I keep the div open while either the table or the input have focus?

Upvotes: 2

Views: 593

Answers (3)

JotaBe
JotaBe

Reputation: 39014

A table isn't focusable by default, so that your first idea will not work. However, if you add a tabindex attribute, you can safely set the focus on the table. Thus, if you do so, you can use a focus observable for each element, and a computed which determines if either of them is focused to keep the div visible.

var vm = {
  inputHasFocus: ko.observable(),
  tableHasFocus: ko.observable(),
};

// Don't take completing a model like this as a good practice:
vm.somethingHasFocus = ko.pureComputed(function() {
  var inputHasFocus = vm.inputHasFocus(); // ensures subscription
  var tableHasFocus = vm.tableHasFocus(); // ensures subscription
  return  inputHasFocus || tableHasFocus;
}, vm);

ko.applyBindings(vm);

// IE Hack
$('table *').on('click', function() { vm.tablehasFocus(true); })
table {
  border: solid 1px black;
  border-collapse: true;
  width: 80%;
  margin: 20px 0;
}

*:focus {
  border-color: #FF0000;
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6);
  outline: none;
}

div {
  background-color: silver;
  margin: 20px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<input tabindex="0" type="text" data-bind="hasFocus: inputHasFocus"/>


<table tabindex="1" data-bind="hasFocus: tableHasFocus">
  <tr>
    <td>I'm a table cell</td>
    <td>I'm another</td>
  <tr>
</table>

<div data-bind='visible: inputHasFocus'>Input has focus</div>
<div data-bind='visible: tableHasFocus'>Table has focus</div>
<div data-bind='visible: somethingHasFocus'>Something has focus</div>

NOTE: as usual IE has a strange behavior: at least IE10 allows to focus on a table cell. The solution included in the script it's a hack. There are smarter options:

  • create a custom binding elementOrDescendantHasFocus, which should not be too difficult: you can use delegated event handling to detect the focus events in the table or children
  • create an array of hasFocus for each table cell, and a computed that depends on them all (I'd rather implement the hack)
  • make the cells unfocusable by adding a negative tabindex attribute to them

So, if you have to support IE (at least IE10), choose any of these solutions, including the hack. In Chrome and Firefox there is no such problem

Upvotes: 2

Lai32290
Lai32290

Reputation: 8548

Maybe you can solver this question using focus event, like fallowing example:

http://jsbin.com/jewozilofe/edit?html,js,output

Upvotes: 1

indubitablee
indubitablee

Reputation: 8206

idk if you want to do it this way, but you can make it so the table opens on focus of the input and stays open until you close it.

// Here's my data model
var ViewModel = function() {
    var self = this;
    
    self.focusMePlease = ko.observable(false);
    self.focusMePlease.subscribe(function() {
    	if (self.focusMePlease() == true) {
        	self.isSearchInputFocused(true);
        }
    });
    self.isSearchInputFocused = ko.observable(false);
    
    self.close = function() {
    	self.isSearchInputFocused(false);
    };
    
};
 
ko.applyBindings(new ViewModel());
td {
    border: 1px solid black;
    padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script><input id="usersInputField" data-bind="hasFocus: focusMePlease"/>
<br/><br/>
<div data-bind="visible: isSearchInputFocused">
   <table>
       <tr><td>stuff</td><td>stuff</td></tr>
   </table>
    <button data-bind="click: close">X</button>
</div>

Upvotes: 1

Related Questions