Originally Posted by
MutantJohn
Update : I've thought about this a little bit more so hopefully I can get some feedback.
I liked your ideas, Alpo, and they inspired me.
Let's say we dedicate a keyboard shortcut to bringing up a search bar in the center of the screen. As the user types, all the strings in the table that contain what the user types as a substring show up below it. Sort of like an auto-complete form, is what I'm trying to go for. And then if the user hits enter or clicks on a name from the drop down list, the focus() shifts to the appropriate row and select element. This way, the user can also see nearby or perhaps relevant rows entries.
Does any of this make sense?
Yes, I think I see what you are saying. I tried a small implementation out, and it seems pretty decent. The main crinkle I had working it out is what criteria is used to tell if a search matches.
In this example, I just take whatever is in the search bar, and check it against table elements using the String.prototype.indexOf method, so it might be more or less indiscriminate than you would want. Some other possible methods would be to slice the strings to the same length and perform an equality check (that could be too strict), or to make a RegExp object out of the search-bar value.
I used just regular div elements to build the "autocomplete" list:
Code:
$(function () {
"use strict";
// The same function to build a table:
var fillTable = (function () {
var names = ["John Addams", "Jane Doe", "Bobby miller", "Al Bundy"],
options = ["yellow", "blue", "orange", "red", "grey"],
select = $("<select></select>");
return function (height) {
var table = $("#table1"),
tr,
td,
sel,
i,
j;
for (i = 0; i < height; i++) {
tr = $("<tr></tr>").append($("<td>" + names[Math.floor(Math.random() * names.length)] + "</td>"));
td = $("<td></td>");
sel = select.clone();
for (j = 0; j < 5; j++) {
sel.append($("<option>" + options[Math.floor(Math.random() * options.length)] + "</option>"));
}
td.append(sel);
tr.append(td);
table.append(tr);
}
};
}());
// The search shortcut listener:
$("body").bind("keydown", function (event) {
var key = event.key,
selects = $("#table1 tr td select"),
search = $("#search"),
currentIndex = selects.index(event.target),
// I just chose a random search shortcut "Ctrl + x"
searchShortcut = function () {
return (key === "x" && event.ctrlKey ? true : false);
};
// Check if we are on an element within the table, and that we've hit the shortcut
if (currentIndex !== -1 && searchShortcut()) {
// Make the search div visible:
search.toggle(300);
// Make sure any previous searches are clear:
search.children("#searchbar").val("");
search.children("#results").html("");
}
});
// The logic to build an autocomplete list, and to take you to the selected table element:
$("#search > #searchbar").bind("keyup", function (event) {
event.stopPropagation();
// Collect some values for later use
// The searchbar value:
var currentSearch = $(this).val(),
// A wrapped set of all td elements with names of people:
people = $("#table1 tr td:first-child"),
// A wrapped set of select elements beside the people:
selects = $("#table1 tr td select"),
// The search div:
search = $("#search"),
// The searchbar (bound to this):
searchbar = $(this),
// The div that will contain divs containing the results:
results = $("#search > #results"),
// A variable to build matches out of:
newMatch,
// An array to collect search matches into:
matches = [],
// A callback for when user clicks a search result:
matchSelect = function (event) {
// Get the index of the clicked element within the results:
var index = results.children("div").index(this),
chosen;
// Check if clicked element was within the results set (may be redundant)
if (index !== -1) {
// Get the object that specifies where in our table the match is:
chosen = matches[index];
// Hide the searchbar and clear search.
search.toggle(300);
search.children("#searchbar").val("");
search.children("#results").html("");
// switch the focus to the chosen element.
selects.get(chosen.index).focus();
}
};
// We want new results every time the user presses a key:
results.html("");
// Go through the list of people, check their names against the search-bar value:
people.each(function (n) {
if (this.innerHTML.toLowerCase().indexOf(currentSearch.toLowerCase()) !== -1) {
// If we get here a match was found, so push back its index within the table:
matches.push({match: this.innerHTML, index: n});
// Build an element the user can click on:
newMatch = $("<div>" + this.innerHTML + "</div>").css("cursor", "pointer");
// Bind our click handler, and then append the newMatch element:
newMatch.bind("click", matchSelect);
$("#search > #results").append(newMatch);
}
});
});
fillTable(18);
});
For the search bar, I just went for a pretty simple "template" type of idea, where it starts out hidden, and is toggled to visible by the user hitting the shortcut, it goes back to being hidden when a selection is made:
Code:
<div id="search" style="display: none; border: 1px solid black;">
<label for="searchbar">Search: </label>
<input type="text" id="searchbar"/>
<div id="results"></div>
</div>
Anyway it seems like a pretty decent idea, I don't know if you would want to use this implementation though (it only took 45 minutes or so, so there are probably much better ones lol).
Edit:
Also I forgot to mention. The reason I bind'ed the search-bar to "keyup" was for when the user begins deleting letters. If it is listening for "keypress", for some reason the backspace key did not trigger the event on any browser I tried it on.
The "keydown" event isn't as useful here either, as when we've just deleted a key, we want the current search-bar value, rather than the value before the letter was deleted (if that makes sense).