タグのリストにjQueryを追加していないOPのために(おめでとうございます)、純粋なJavaScriptで小さなナビゲーションプラグインを追加します。
開示-実際のロジックの功績は、Facebookで有名なEric Priestleyに与えられます。これは、彼のJavelinJSライブラリから削除されたためです。
ここにフィドルへのリンクを投稿しています。また、あなたの質問から、あなたはすでに検索ソリューションを導入していて、単にナビゲーションソリューションを探していると思います。
フィドルからのクロスポスト。私はコードでたくさんコメントしたので、自明であるはずです。疑問がある場合は、連絡してください。
HTML
はあなたが望むものなら何でもかまいません。簡潔にするために、これを追加しました
<input id='control' type='text' placeholder='Search text here' />
<!-- List of results - hardcoded for now -->
<ul id='hardpoint' class='hidden'>
<li class='item'>Search item 1</li>
<li class='item'>Search item 2</li>
<li class='item'>Search item 3</li>
<li class='item'>Search item 4</li>
</ul>
コード
(function() {
/**
* Helper functions
*/
var bind = function( context, func, more ){
if (typeof func !== 'function') {
throw new Error ("No function supplied to bind function");
}
var bound = Array.prototype.slice.call( arguments ).slice(2);
if (func.bind) {
return func.bind.apply(func, [context].concat(bound));
}
return function() {
return func.apply(context || window, bound.concat(Array.prototype.slice.call( arguments )));
}
};
var getChildElements = function(node) {
var childNodes = Array.prototype.slice.call(node.childNodes);
childNodes = childNodes.filter(function(item) {
return item.tagName && item.nodeType && item.nodeType === 1 && item !== node;
});
return childNodes;
};
var hasClass = function(node, className) {
return ((' ' + node.className + ' ').indexOf(' ' + className + ' ') > -1);
};
var alterClass = function(node, className, add) {
var has = hasClass(node, className);
if (add && !has) {
node.className = node.className.trim() + ' ' + className;
}
else if (has && !add) {
node.className = node.className.replace(
new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), ' ');
}
};
var getIndex = function( list, element ){
var index = -1;
for (var i = 0; i < list.length; i++) {
if( list[i] === element ){
index = i;
break;
}
};
return index;
};
// Start of plugin
// Constructor
var ListNavigator = function(config) {};
// This can be moved within constructor if you so prefer
ListNavigator.prototype.init = function(config) {
// On what element do you want the controls to activate
// Pressing up down etc on this element, triggers tha navigation
this.control = document.getElementById(config.control);
// The list of results that can be navigated
this.hardpoint = document.getElementById(config.hardpoint);
// A list of items ( usually, childNodes ) of hardpoint
// Dynamically populated
this.display = getChildElements(this.hardpoint);;
// What to set the focus on initially
this.focus = -1;
// Specify a function to execute when the user chooses a result
// Keydown - Return : configured to be the choose event type now
this.choose = config.choose;
this.selector = config.selector;
};
ListNavigator.prototype.run = function() {
var controlEvents = ['focus', 'blur', 'keydown', 'input'],
mouseEvents = [ 'mouseover', 'mouseout', 'mousedown' ],
self = this,
selector = this.selector;
controlEvents.forEach(function(event) {
self.control.addEventListener(event, bind(self, self.handleEvent), false);
});
mouseEvents.forEach(function(event) {
self.hardpoint.addEventListener(event, bind(self, self.onmouse), true);
});
};
// Logic to change the focus on keydown
ListNavigator.prototype.changeFocus = function(d) {
var n = Math.min( Math.max( -1, this.focus + d ), this.display.length - 1);
if (this.focus >= 0 && this.focus < this.display.length) {
alterClass(this.display[this.focus], 'focused', false);
}
this.focus = n;
this.drawFocus();
return true;
};
// Set the focus on the targetted element
ListNavigator.prototype.drawFocus = function() {
var f = this.display[this.focus];
if (f) {
alterClass(f, 'focused', true);
}
};
// Handle mouse events
ListNavigator.prototype.onmouse = function(event) {
var target = event.target, type = event.type;
if ( hasClass( target, this.selector ) ) {
if ( type === 'mousedown' ) {
// Choose this element
this.choose(target);
}
else if ( type === 'mouseover' ) {
// Set the focus to element on which mouse is hovering on
this.focus = getIndex( this.display, target );
this.drawFocus();
}
else if ( type === 'mouseout' ){
// Reset the display to none
this.changeFocus(Number.NEGATIVE_INFINITY);
}
};
};
ListNavigator.prototype.handleEvent = function(e) {
var type = e.type;
if (type === 'blur') {
this.focused = false;
this.hide();
} else {
alterClass(this.hardpoint, 'hidden', false);
this.update(e);
}
};
ListNavigator.prototype.hide = function() {
this.changeFocus(Number.NEGATIVE_INFINITY);
this.display = [];
alterClass(this.hardpoint, 'hidden', true);
};
ListNavigator.prototype.submit = function() {
if (this.focus >= 0 && this.display[this.focus]) {
this.choose(this.display[this.focus]);
} else {
if (this.display.length) {
this.changeFocus(1);
this.choose(this.display[this.focus]);
}
}
return false;
};
ListNavigator.prototype.update = function(event) {
console.log( 'upadte' );
this.display = getChildElements(this.hardpoint);
if (event.type === 'focus') {
this.focused = true;
}
var k = event.which;
if (k && event.type == 'keydown') {
switch (k) {
case 38:
if (this.display.length && this.changeFocus(-1)) {
event.stopPropagation();
}
break;
case 40:
if (this.display.length && this.changeFocus(1)) {
event.stopPropagation();
}
break;
case 13:
if (this.display.length) {
this.hide();
event.stopPropagation();
this.submit();
}
break;
case 27:
if (this.display.length) {
this.hide();
event.stopPropagation();
}
break;
case 9:
// If the user tabs out of the field, don't refresh.
return;
}
}
};
window.ListNav = ListNavigator
})();
var nav = new ListNav();
nav.init({
control: 'control',
hardpoint: 'hardpoint',
selector: 'item',
choose: function(){
console.log( arguments );
}
});
nav.run();