3

我正在创建一个我希望用户能够使用键盘与之交互的项目列表。所以像这样的事情......

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

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

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

编辑

感谢 jumpingcode,以下工作使其保持只读状态......

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

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

编辑

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

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

2 回答 2

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

这是一个工作演示

于 2013-10-21T14:08:34.667 回答
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 键顺序。

于 2013-10-18T14:37:46.637 回答