使用击键导航列表

Joh*_*ore 3 html javascript css jquery

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

<ul contenteditable="true">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>
Run Code Online (Sandbox Code Playgroud)

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

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

编辑

感谢jumpcode,以下工作将其保持为只读...

$('ul').on('click', 'li', function (e) {
    e.preventDefault();
});
Run Code Online (Sandbox Code Playgroud)

......但是我仍然留着一个闪烁的光标.我怎么摆脱它?

编辑

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

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

Car*_*los 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);
};
Run Code Online (Sandbox Code Playgroud)

使用它非常简单:

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;
});
Run Code Online (Sandbox Code Playgroud)

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

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

对于跨浏览器兼容性,您需要在所有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;
  };
}
Run Code Online (Sandbox Code Playgroud)

这是一个工作演示.