将可聚焦控件限制为当前对话框的最佳方法是什么?

时间:2014-06-05 09:33:00

标签: javascript jquery html css

我有一个带对话框的Web应用程序。对话框是附加到正文的简单div容器。整个页面还有一个叠加层,以防止点击其他控件。但是:目前用户可以聚焦覆盖下的控件(例如输入)。有没有办法将tabbable控件限制为对话框中的控件?

我正在使用jQuery(但现在是jQueryUI)。在jQueryUi对话框中,它正在工作(但我 想要使用jQueryUI)。我没弄明白,这是如何实现的。

以下是jQueryUI示例:http://jqueryui.com/resources/demos/dialog/modal-confirmation.html - 网页上的链接无法调整。焦点保留在对话框内(用户无法使用选项卡聚焦浏览器的urlbar)。

HTML:

<a href="#test" onclick="alert('Oh no!');">I should not receive any focus</a>
<input type="text" value="No focus please" />
<div class="overlay">
    <div class="dialog">
        Here is my dialog<br />
        TAB out with Shift+Tab after focusing "focus #1"<br />
        <input type="text" value="focus #1" /><br />
        <input type="text" value="focus #1" /><br />
    </div>
</div>    

CSS:

.overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.3);
    text-align: center;
}

.dialog {
    display: inline-block;
    margin-top: 30%;
    padding: 10px;
    outline: 1px solid black;
    background-color: #cccccc;
    text-align: left;
}

这是我的小提琴:http://jsfiddle.net/SuperNova3000/weY4L/

有人有想法吗?我再说一遍:我不想为此使用jQueryUI。我想了解基本技术。

2 个答案:

答案 0 :(得分:4)

经过数小时的努力,我找到了解决这个问题的简单方法。我认为最好的方法是添加2个伪元素。对话框之前和之后(在叠加层内)。我使用&lt; a&gt; -Tags是0x0像素。当到达第一个&lt; a&gt;时,我将最后一个控件聚焦在对话框中。当聚焦最后一个&lt; a&gt;时,我将第一个控件聚焦在对话框中。

我已经调整了这篇文章的答案:Is there a jQuery selector to get all elements that can get focus? - 找到第一个也是最后一个可聚焦的控件。

HTML:

<div class="overlay">
    <a href="#" class="focusKeeper">
    <div class="dialog">
        Here is my dialog<br />
        TAB out with Shift+Tab after focusing "focus #1"<br />
        <input type="text" value="focus #1" /><br />
        <input type="text" value="focus #1" /><br />
    </div>
    <a href="#" class="focusKeeper">
</div>    

额外的CSS:

.focusKeeper {
    width: 0;
    height: 0;
    overflow: hidden;
}

我的Javascript:

$.fn.getFocusableChilds = function() {
  return $(this)
    .find('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object:not([disabled]), embed, *[tabindex], *[contenteditable]')
    .filter(':visible');
};

[...]

$('.focusKeeper:first').on('focus', function(event) {
    event.preventDefault();
    $('.dialog').getFocusableChilds().filter(':last').focus();
});

$('.focusKeeper:last').on('focus', function(event) {
    event.preventDefault();
    $('.dialog').getFocusableChilds().filter(':first').focus();
});

可能是我以后添加一个小提琴,暂时不再有时间。 :(


编辑:正如KingKing在下面指出的那样,当在控件外面点击时,焦点会丢失。这可以通过为.overlay添加一个mousedown处理程序来实现:

$('.overlay').on('mousedown', function(event) {
    event.preventDefault();
    event.stopImmediatePropagation();
});

编辑#2:还有另外一件事:用焦点(例如标题栏)走出文档,然后回到标签。所以我们需要另一个文档处理程序,它将焦点放在第一个可聚焦元素上:

$(document).on('focus', function(event) {
    event.preventDefault();
    $('.dialog').getFocusableChilds().filter(':first').focus();
});

答案 1 :(得分:2)

您可以尝试处理focusout元素的.dialog事件,查看e.target。关于此处e.relatedTarget的注释,它指的是获得焦点的元素,而e.target指的是元素丢失焦点:

var tabbingForward = true;
//The body should have at least 2 input fields outside of the dialog to trap focusing,
//otherwise  focusing may be outside of the document 
//and we will loss control in such a case.
//So we create 2 dummy text fields with width = 0 (or opacity = 0)
var dummy = "<input style='width:0; opacity:0'/>";
var clickedOutside = false;
$('body').append(dummy).prepend(dummy);
$('.dialog').focusout(function(e){     
  if(clickedOutside) { 
    e.target.focus();
    clickedOutside = false;
  }
  else if(!e.relatedTarget||!$('.dialog').has(e.relatedTarget).length) {   
    var inputs = $('.dialog :input');
    var input = tabbingForward ? inputs.first() : inputs.last();
    input.focus();        
  }
}); 
$('.dialog').keydown(function(e){
  if(e.which == 9) {
    tabbingForward = !e.shiftKey;
  }
});
$('body').mousedown(function(e){
  if(!$('.dialog').has(e.target).length) {        
    clickedOutside = true;        
  }
});

Demo.