jQuery UI - 点击对话框外部时关闭对话框

122
当特定元素被点击时,我有一个jQuery UI对话框会显示出来。如果除了这些触发元素或对话框本身之外的任何地方都被点击了,我想关闭对话框。
以下是打开对话框的代码:
$(document).ready(function() {
    var $field_hint = $('<div></div>')
        .dialog({
            autoOpen: false,
            minHeight: 50,
            resizable: false,
            width: 375
        });

    $('.hint').click(function() {
        var $hint = $(this);
        $field_hint.html($hint.html());
        $field_hint.dialog('option', 'position', [162, $hint.offset().top + 25]);
        $field_hint.dialog('option', 'title', $hint.siblings('label').html());
        $field_hint.dialog('open');
    });
    /*$(document).click(function() {
        $field_hint.dialog('close');
    });*/
});

如果我取消最后一部分的注释,对话框将永远不会打开。我认为这是因为打开对话框的点击也再次关闭了它。

最终工作代码
注意:这里使用了jQuery外部事件插件

$(document).ready(function() {
    // dialog element to .hint
    var $field_hint = $('<div></div>')
            .dialog({
                autoOpen: false,
                minHeight: 0,
                resizable: false,
                width: 376
            })
            .bind('clickoutside', function(e) {
                $target = $(e.target);
                if (!$target.filter('.hint').length
                        && !$target.filter('.hintclickicon').length) {
                    $field_hint.dialog('close');
                }
            });

    // attach dialog element to .hint elements
    $('.hint').click(function() {
        var $hint = $(this);
        $field_hint.html('<div style="max-height: 300px;">' + $hint.html() + '</div>');
        $field_hint.dialog('option', 'position', [$hint.offset().left - 384, $hint.offset().top + 24 - $(document).scrollTop()]);
        $field_hint.dialog('option', 'title', $hint.siblings('label').html());
        $field_hint.dialog('open');
    });

    // trigger .hint dialog with an anchor tag referencing the form element
    $('.hintclickicon').click(function(e) {
        e.preventDefault();
        $($(this).get(0).hash + ' .hint').trigger('click');
    });
});
21个回答

168

非常抱歉这么久之后再提这个问题,但我使用了以下代码。有什么缺点吗?请看open函数...

$("#popup").dialog(
{
    height: 670,
    width: 680,
    modal: true,
    autoOpen: false,
    close: function(event, ui) { $('#wrap').show(); },
    open: function(event, ui) 
    { 
        $('.ui-widget-overlay').bind('click', function()
        { 
            $("#popup").dialog('close'); 
        }); 
    }
});

18
仅当UI窗口是模态的时,该方法才能生效。如果您想关闭一个模态对话框,那么这将会很有用。 - stumac85
39
很好。我只是将其更改为以下内容,以便不必显式设置ID引用:$('.ui-widget-overlay').bind('click', function () { $(this).siblings('.ui-dialog').find('.ui-dialog-content').dialog('close'); }); - James McCormack
1
我喜欢这个。有没有一种情况,你不想要它是模态的,但仍然希望点击外部关闭?对我来说没有意义(我猜想使用模态时,你会失去在外部/下方元素上悬停的功能)。 - Nick Spacek
3
当它不是模态的时候,我可以用一个点击将焦点设置到一个字段、打开一个新的对话框等。但是如果是模态对话框,我需要使用两个点击:一个关闭它,另一个执行下一步操作。 - Sonny
1
谢谢!您还可以利用jQuery的事件冒泡。$('body').on('click', '.ui-widget-overlay', close); - Quang Van
显示剩余5条评论

83

不要使用其他插件:

以下是3种方法,可以在单击弹出框外部时关闭jquery UI对话框:

如果对话框是模态的/有背景叠加层:http://jsfiddle.net/jasonday/6FGqN/

jQuery(document).ready(function() {
    jQuery("#dialog").dialog({
        bgiframe: true,
        autoOpen: false,
        height: 100,
        modal: true,
        open: function(){
            jQuery('.ui-widget-overlay').bind('click',function(){
                jQuery('#dialog').dialog('close');
            })
        }
    });
}); 

如果对话框是非模态的,方法一:方法1:http://jsfiddle.net/jasonday/xpkFf/

 // Close Pop-in If the user clicks anywhere else on the page
                     jQuery('body')
                      .bind(
                       'click',
                       function(e){
                        if(
                         jQuery('#dialog').dialog('isOpen')
                         && !jQuery(e.target).is('.ui-dialog, a')
                         && !jQuery(e.target).closest('.ui-dialog').length
                        ){
                         jQuery('#dialog').dialog('close');
                        }
                       }
                      );

非模态对话框方法2: http://jsfiddle.net/jasonday/eccKr/

  $(function() {
            $( "#dialog" ).dialog({
                autoOpen: false, 
                minHeight: 100,
                width: 342,
                draggable: true,
                resizable: false,
                modal: false,
                closeText: 'Close',
                  open: function() {
                      closedialog = 1;
                      $(document).bind('click', overlayclickclose);
                  },
                  focus: function() {
                      closedialog = 0;
                  },
                  close: function() {
                      $(document).unbind('click');
                  }



        });

         $('#linkID').click(function() {
            $('#dialog').dialog('open');
            closedialog = 0;
        });

         var closedialog;

          function overlayclickclose() {
              if (closedialog) {
                  $('#dialog').dialog('close');
              }

              //set to one because click on dialog box sets to zero
              closedialog = 1;
          }


  });

2
太好了!我稍微修改了模态对话框的打开选项函数,因此不需要显式命名元素。open: function() { $('.ui-widget-overlay').on('click', function() { $(this).parents("body").find(".ui-dialog-content").dialog("close"); }); } - meridius
请注意,对于解决方案#2,.is('.ui-dialog,a')必须更改为.is('.ui-dialog,whateverYouClickOnToOpenTheDialog')。 - personne3000
@Jason 因为逗号的缘故,我认为这行代码实际上是在说“不是 ui-dialog,也不是页面中的任何链接”。如果我将你示例中的“打开对话框”链接更改为<span>,则对话框会立即关闭,因为窗口事件是最后触发的,这就是为什么我认为您需要排除单击以打开对话框的项目。我不明白为什么您需要引用对话框中的链接? - personne3000
@personne3000 - 实际上你关于上下文的理解是正确的,选择器正在同时选择两个元素。我试图回想一下为什么我添加了这个,因为我当时肯定有一个具体的原因,但现在却不记得了。 - Jason
@Jason 为避免多个对话框之间的冲突,您可以使用命名空间事件 click.myNamespace - Christophe Roussy
显示剩余3条评论

32

看看jQuery外部事件插件

让你可以:

$field_hint.bind('clickoutside',function(){
    $field_hint.dialog('close');
});

我遇到了相同的问题,即当单击$('.hint')元素时,提示不会显示。这些元素是在对话框之外的。 - Sonny
只有在对话框打开时,您才需要关注点击外部。因此只有在打开对话框后才进行绑定。 - PetersenDidIt
3
我在另一个地方读到了关于基于事件进行过滤的内容,这解决了问题:http://groups.google.com/group/jquery-ui/msg/a880d99138e1e80d - Sonny
对话框在文档中被多次重复使用,因此我需要一种方法在关闭对话框时解除绑定。我认为过滤是一个更简单的解决方案。 - Sonny

18

只需添加此全局脚本,即可通过单击模态对话框外部来关闭所有模态对话框。

$(document).ready(function()
{
    $(document.body).on("click", ".ui-widget-overlay", function()
    {
        $.each($(".ui-dialog"), function()
        {
            var $dialog;
            $dialog = $(this).children(".ui-dialog-content");
            if($dialog.dialog("option", "modal"))
            {
                $dialog.dialog("close");
            }
        });
    });;
});

我没有使用模态对话框。这里获得最多赞的答案也是针对模态对话框的。 - Sonny
当在同一页上多次使用相同的对话框时,这是唯一可行的方法,因为如果你将其绑定在打开函数中,它只能工作一次。感谢这个好主意! - MaDaHoPe
$(document).on('click', '.ui-widget-overlay', function() { $('#'+$('.ui-dialog-content')[0].id).dialog('close'); }); 这段代码是用来关闭一个 UI 对话框的。当用户点击 UI 遮罩层时,会触发 click 事件,然后通过获取对话框内容的 ID 来关闭对话框。 - mr5

12

1
我使用了这个解决方案。它很简单! - Carlos Espinoza

8

我需要完成两部分任务。首先是处理外部点击的函数:

$(document).on('click', function(e){
    if ($(".ui-dialog").length) {
        if (!$(e.target).parents().filter('.ui-dialog').length) {
            $('.ui-dialog-content').dialog('close');
        }
    }
}); 

这会在通用的ui-dialog-content类上调用dialog('close'),因此如果单击事件不是从对话框发起的,则会关闭所有对话框。它也适用于模态对话框,因为叠加层不是.ui-dialog的一部分。

问题在于:

  1. 大多数对话框是由对话框外的点击事件创建的
  2. 这个处理程序在这些点击已经创建了一个对话框并上升到文档之后运行,所以它会立即关闭它们。

为了解决这个问题,我不得不在这些点击处理程序中添加stopPropagation:

moreLink.on('click', function (e) {
    listBox.dialog();
    e.stopPropagation(); //Don't trigger the outside click handler
});

这听起来比我正在使用的解决方案更简单。我得试一下。 - Sonny
这是我自己想出的解决方案,但我的代码只有一行:$('body').on('click', '.ui-widget-overlay', function () { $('#myDialog').dialog('close'); }); - styfle

5
您可以在不使用任何额外插件的情况下完成此操作。
var $dialog= $(document.createElement("div")).appendTo(document.body);
    var dialogOverlay;

    $dialog.dialog({
        title: "Your title",
        modal: true,
        resizable: true,
        draggable: false,
        autoOpen: false,
        width: "auto",
        show: "fade",
        hide: "fade",
        open:function(){
            $dialog.dialog('widget').animate({
                width: "+=300", 
                left: "-=150"
            });

//get the last overlay in the dom
            $dialogOverlay = $(".ui-widget-overlay").last();
//remove any event handler bound to it.
            $dialogOverlay.unbind();
            $dialogOverlay.click(function(){
//close the dialog whenever the overlay is clicked.
                $dialog.dialog("close");
            });
        }
    });

这里的$dialog是对话框。 我们基本上所做的就是在每次打开此对话框时获取最后一个叠加小部件,并将单击处理程序绑定到该叠加层,以便在单击叠加层时关闭$dialog。


我认为这与模态对话框的其他解决方案类似。我的问题是针对非模态对话框的。 - Sonny

5

这个问题有点老了,但是如果有人想要关闭一个非模态对话框,当用户点击其他地方时,你可以使用我从JQuery UI多选插件中学到的方法。主要优点是点击不会“丢失”(如果用户想要单击链接或按钮,则动作可以执行)。

$myselector.dialog({
            title: "Dialog that closes when user clicks outside",
            modal:false,
            close: function(){
                        $(document).off('mousedown.mydialog');
                    },
            open: function(event, ui) { 
                    var $dialog = $(this).dialog('widget');
                    $(document).on('mousedown.mydialog', function(e) {
                        // Close when user clicks elsewhere
                        if($dialog.dialog('isOpen') && !$.contains($myselector.dialog('widget')[0], e.target)){
                            $myselector.dialog('close');
                        }            
                    });
                }                    
            });

我不得不将 var $dialog = $(this).dialog('widget'); 移到 on-click 事件处理程序内部。 - Stefan Haberl
1
@Melanie,我认为你的解决方案比其他人更适用。有个人基于你的方法创建了一个“jqui dialog”插件 - js at github - resnyanskiy

5
不需要使用外部事件插件...
只需向 .ui-widget-overlay div 添加事件处理程序:
jQuery(document).on('click', 'body > .ui-widget-overlay', function(){
     jQuery("#ui-dialog-selector-goes-here").dialog("close");
     return false;
});

请确保您用于jQuery ui对话框的任何选择器也用于关闭它...即#ui-dialog-selector-goes-here


已经提出了几种关闭模态对话框的解决方案。我的对话框是非模态的,因此没有覆盖层。 - Sonny
你只需要将点击事件绑定到 body 标签或 div 包装器上,并将其用作点击事件触发器,而不是模态框。 - Jonathan Marzullo
是的,这基本上就是我的解决方案所做的。它还必须排除对话框内的点击。 - Sonny

3

虽然这并没有使用jQuery UI,但却使用了jQuery,对于那些由于特定原因不使用jQuery UI的人来说可能是有用的。可以按照以下方式完成:

function showDialog(){
  $('#dialog').show();
  $('*').on('click',function(e){
    $('#zoomer').hide();
  });
}

$(document).ready(function(){

  showDialog();    

});

因此,一旦我展示了对话框,我就添加了一个点击处理程序,只寻找任何东西的第一次点击。

现在,如果我能让它忽略对#dialog及其内容的任何点击,那就更好了,但是当我尝试将$('*')与$(':not("#dialog,#dialog *")')切换时,它仍然会检测到#dialog的点击。

无论如何,我仅将其用于照片灯箱,所以它还可以胜任那个目的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接