使用击键导航列表

时间:2013-10-18 14:20:48

标签: javascript jquery html css

我正在创建一个项目列表,我希望用户能够使用键盘进行交互。所以像这样......

<ul contenteditable="true">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>

我的理解是为了捕获keydown / keyup事件,我必须设置contenteditable属性。这有效,但现在li的内容是可编辑的,我不想要。我只需要捕获keydown / keyup事件。

如何在不使内容可编辑的情况下捕获这些事件?

修改

感谢jumpcode,以下内容可以保持只读...

$('ul').on('click', 'li', function (e) {
    e.preventDefault();
});

...但是我仍然留在一个闪烁的光标上。我该如何摆脱它?

修改

开始赏金,需要对以下的preventDefault答案进行以下更新。

  1. 如何摆脱闪烁的光标?
  2. 如何制作不可选择的?我真的只想捕获keydown事件!如果我能做到这一点而不设定可能更适合的那种。
  3. 我可以在文档级捕获keydown,但问题就变成了keydown从哪里发出的?可能有几个插件正在运行,所有插件都需要响应同一事件,但只有在它们是活动范围时才会响应。

2 个答案:

答案 0 :(得分:5)

正如您所说,您无需编辑列表项,因此没有理由使用contenteditable

您需要的是一个与列表的当前活动项对应的索引,以及按下箭头时递增和递减索引的函数。这是一个构造函数,它实现了您需要的功能:

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);
};

使用它非常简单:

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;
});

另外,我在列表中实现了一个change事件,您可以这样使用:

$('#list').change(function (e) {
    console.log('List. Selected: ' + e.index);
});
$('#list2').change(function (e) {
    console.log('List 2. Selected: ' + e.index);
});

对于跨浏览器兼容性,您需要在所有Javascript代码之前使用此功能:

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;
  };
}

这是working demo

答案 1 :(得分:0)

试试这个。

<ul contenteditable="true">
   <li tabindex="1">Item 1</li>
   <li tabindex="2">Item 2</li>
   <li tabindex="3">Item 3</li>
</ul>

Tabindex将设置HTML中元素的Tab键顺序。