Reputation: 31333
I am creating a list of items that I want the user to be able to interact with using the keyboard. So something like this...
<ul contenteditable="true">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
My understanding is in order to capture keydown/keyup events, I have to set the contenteditable attribute. This works, but now the contents of the li's are editable which I don't want. I simply need to capture keydown/keyup events.
How can I capture these events without making the content editable?
EDIT
Thanks to jumpingcode, the following works to keep it read-only...
$('ul').on('click', 'li', function (e) {
e.preventDefault();
});
... but I am still left with a blinking cursor on the li. How do I get rid of that?
EDIT
Started bounty, need the following updates to the preventDefault answer below.
Upvotes: 3
Views: 159
Reputation: 5072
As you said, you do not need to edit the list items, so there is no reason to use contenteditable
.
What you need is an index that corresponds to the current active item of the list and functions for increment and decrement that index when an arrow is pressed. Here is a constructor function that implements the functionality you need:
var NavigableList = function (ul, selectedClass, activeClass) {
var list = null,
index = 0,
walk = function (step) {
var walked = true;
index += step;
if (index < 0 || index === list.length) {
index -= step;
walked = false;
}
return walked;
},
active = false;
this.active = function (state) {
if (state !== active) {
active = state;
if (active) {
ul.addClass(activeClass);
} else {
ul.removeClass(activeClass);
}
}
};
this.isActive = function () {
return active;
};
this.down = function () {
var previous = index,
changed = false;
if (walk(1)) {
$(list[previous]).removeClass(selectedClass);
$(list[index]).addClass(selectedClass);
changed = true;
}
return changed;
};
this.up = function () {
var previous = index,
changed = false;
if (walk(-1)) {
$(list[previous]).removeClass(selectedClass);
$(list[index]).addClass(selectedClass);
changed = true;
}
return changed;
};
this.currentIndex = function () {
return index;
};
this.currentElementx = function () {
return $(list[index]);
};
ul = $(ul);
list = ul.find('>li');
selectedClass = selectedClass || 'current';
activeClass = activeClass || 'active';
$(ul).click(function (e) {
this.active(true);
NavigableList.activeList = this;
}.bind(this));
$(document).keydown(function(e) {
var event = $.Event('change');
if (this.isActive() && e.keyCode === 40) {
if (this.down()) {
event.selected = $(list[index]);
event.index = index;
ul.trigger(event);
}
} else if (this.isActive() && e.keyCode === 38) {
if (this.up()) {
event.selected = $(list[index]);
event.index = index;
ul.trigger(event);
}
}
}.bind(this));
$(list[index]).addClass(selectedClass);
};
Using this is quite easy:
var list = new NavigableList($('#list'), 'current', 'active'),
list2 = new NavigableList($('#list2'));
$(document).click(function (e) {
if (NavigableList.activeList) {
switch (NavigableList.activeList) {
case list:
list2.active(false);
break;
case list2:
list.active(false);
break;
}
} else {
list.active(false);
list2.active(false);
}
NavigableList.activeList = null;
});
Also, I implemented a change
event on the list that you can use this way:
$('#list').change(function (e) {
console.log('List. Selected: ' + e.index);
});
$('#list2').change(function (e) {
console.log('List 2. Selected: ' + e.index);
});
For cross-browser compatibility you'll need this before all Javascript code:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
Here is a working demo.
Upvotes: 5
Reputation: 390
Try this.
<ul contenteditable="true">
<li tabindex="1">Item 1</li>
<li tabindex="2">Item 2</li>
<li tabindex="3">Item 3</li>
</ul>
Tabindex will set the tab order of elements in your HTML.
Upvotes: 0