1

假设您的 HTML 中具有以下结构,并且内容 div 中的某些元素具有各种键(按下、向上、向下)事件:

<html>
<head></head>
<body>
    <div id="content">All the content</div>
</body>
</html>

您通过 jQuery 添加一个绝对定位的 div:

$('body').append('<div id="lightbox">etc.</div>');

您如何临时(在灯箱存在期间)防止所有键盘事件被 $('#content')及其子级捕获或冒泡,以便上述结构的灯箱存在时的冒泡顺序将是

  1. $('#lightbox *')
  2. $('#lightbox')
  3. $('body')
  4. $('html')

而它将是

  1. $('#content *')
  2. $('#content')
  3. $('body')
  4. $('html')

灯箱什么时候不见了?

编辑:这里有一些额外的信息。

  • 我不能使所有表单元素都被禁用或只读(这无论如何都适用于有限的元素),因为灯箱有时可能存在于许多已经分配了唯一键事件的表单元素之上。

  • 另外,我希望关键事件影响窗口+(灯箱,如果有,否则为内容),所以我不能直接停止所有事件传播

  • 上面的顺序可以是4-1而不是1-4,没关系。重要的是在关键事件中绕过这个特定元素的内容(灯箱或内容根据情况)。

4

3 回答 3

1

如果我理解正确,您需要在灯箱显示时选择性地禁止关键事件。

我认为最简单的方法是将抑制放在事件处理程序中,如下所示:

  • 当灯箱处于活动/非活动状态时升高/降低标志
  • 在您希望被禁止的事件的所有处理程序中,如果引发标志,则立即返回。

(您可以直接测试灯箱状态,无需使用标志)

我认为这种方法应该比从广义上讲涉及以下任何一种方法的替代方法要容易得多:

  • 防止/允许事件传播
  • 分离和重新附加处理程序。

编辑

Inhan,在阅读了您自己的解决方案之后,这是我保持简单的想法。

制表符

必须有一种更简单的方法来控制标签顺序,而不是在打开灯箱对话框时模拟它。

如果您需要实现的全部效果是抑制选项卡以形成不在当前打开的对话框中的元素(并假设 jQuery UI 灯箱),那么只需设置modal: true选项。对于其他灯箱插件,请参阅他们的文档。

冒泡

您应该能够通过适当地构建您的 HTML 来消除从灯箱到“文档”或任何其他容器的不必要的事件冒泡。尝试遵循以下规则:

  • 不要将任何事件委托给document. 选择更多本地容器。如有必要,将灯箱以外的所有内容包装在 a<div id="pseudoBody">...</div>中,以明确事件委托的目的,即。将事件委托给这个专用的、无样式的包装器,而不是document.
  • 确保您的灯箱没有嵌套在委托事件的容器内,例如。通过将 lightbox(es) 放置在不超过一层的范围内,如果采用<body>的话,肯定在外部。pseudoBody
  • 如果您需要允许一些灯箱事件冒泡,则委托给灯箱容器。pseudoBody为了节省代码(如果合适的话),您可以附加与附加到或其他地方相同的(命名的)事件处理程序。
于 2012-09-14T03:26:05.030 回答
0

我找不到任何可靠的解决方案,所以我选择编写自己的函数。虽然我不太乐意搞砸它,但我必须控制 TAB 键的行为并创建全局 keyup/keydown 函数。

对于那些感兴趣的人,我在 SO、hereherehere中找到了一些资源。

请注意,我为脚本中的锚分配了一个名为“已禁用”的 CSS 类,因此您可以考虑在我的代码中看到的那些,例如disabled="disabled"适用元素的属性。此外,我无法找到一种方法将其从 html 内容集中到浏览器的可选 UI 元素中,但这可能是最后一个问题。

// lightbox keyboard navigation
function getFocusList() {
    return $('body,#lightbox *:visible').filter(function(idx,elm) {
        if ($(elm).is('body')) return true;
        if (/^hidden$/i.test($(elm).css('visibility'))) return false;
        return (
            (/^a(rea)?$/i.test(elm.nodeName) && $(elm).hasAttr('href') && !$(elm).is('.disabled')) ||
            (/^(input|textarea|button|select)$/i.test(elm.nodeName) && !$(elm).prop('disabled')) ||
            (/^\d+$/.test($(elm).prop('tabindex')) && !$(elm).prop('disabled') && !$(elm).is('.disabled'))
        )
    });
}
function globalKeyDown(e) {
    var
        LB          = !!$('#lightbox').length,
        isTAB       = e.which == 9,
        isSHIFT     = e.which == 16,
        SHIFT_ON    = LB && typeof $('#lightbox').data('SHIFT_ON') != 'undefined'
    if (isSHIFT && LB) $('#lightbox').data('SHIFT_ON',true);
    if (!isTAB || !LB) return;
    var focusList = getFocusList(), nextIndex;
    var curIndex = $.inArray($(this).get(0),focusList);
    if (SHIFT_ON) nextIndex = --curIndex == 0 ? focusList.length - 1 : curIndex;
    else nextIndex = ++curIndex == focusList.length ? (focusList.length > 1 ? 1 : 0) : curIndex;
    var next = $(focusList[nextIndex]);
    e.preventDefault();
    e.stopImmediatePropagation();
    next.focus();
}
function globalKeyUp(e) {
    var
        LB          = !!$('#lightbox').length,
        isSHIFT     = e.which == 16,
        SHIFT_ON    = LB && typeof $('#lightbox').data('SHIFT_ON') != 'undefined'
    if (isSHIFT && SHIFT_ON) $('#lightbox').removeData('SHIFT_ON')
}

$.fn.extend({
    hasAttr:function(attrStr) {
        var res = true;
        $(this).each(function() {
            if (typeof $(this).attr(attrStr) == 'undefined') res = false;
        });
        return res;
    }
});
$(document).ready(function() {
    $('body')
    .on('focus','#lightbox a',function() {$(this).addClass('hover')})
    .on('blur','#lightbox a',function() {$(this).removeClass('hover')})
    .on('keydown','*',globalKeyDown).on('keyup','*',globalKeyUp)
    .on('keydown',globalKeyDown).on('keyup',globalKeyUp)
    // remaining jQuery code...
});

我希望这可以为其他有类似问题的人节省一些时间。

于 2012-09-16T02:34:55.443 回答
0

操纵事件的本机传播顺序的唯一方法是更改​​ DOM 顺序。您不能以一种方式离开您的 DOM 结构并指定与 DOM 结构不同的自定义传播顺序。事件在 DOM 中冒泡。这就是浏览器进行事件传播的方式。

您可以使灯箱覆盖整个页面(绝对位置,整个页面的 z-index 高)并将键盘焦点设置为文档,这样除了灯箱和文档之外的其他元素都不会获得输入事件。这通常是使用灯箱覆盖完成的。如果它覆盖了其他元素(即使是部分透明的),那么它将拦截所有鼠标点击。如果您随后将键盘焦点设置为文档,那么键事件将永远不会转到页面中的元素,或者您可以使其以这种方式工作。如果您以这种方式放置灯箱并防止灯箱下方的任何东西获得键盘焦点,那么您应该获得第一个传播顺序。

工作示例:http: //jsfiddle.net/jfriend00/q3r78/

此示例中还包含阻止默认处理击键的代码,因此您无法切换到其他字段。

于 2012-09-13T23:39:16.033 回答