在没有文档/ body-event-listener的情况下单击外部隐藏元素

时间:2012-03-31 22:14:10

标签: jquery drop-down-menu event-listener stoppropagation

我创建了一个非常简单的jQuery“下拉列表”,它由两个div元素组成。单击第一个div时,将显示第二个div。现在我希望第二个div再次关闭,当我点击div之外。但我不能(并且实际上不想,如果没有必要)使用这样的文档或身体事件监听器:

$('#div2').click(function(e) {
    doSomething();
    e.stopPropagation();
});
$(document).click(function() {
    $("#div2").hide();
});

原因:我在页面上有其他使用e.stopPropagation();的元素,如果用户在打开下拉列表后点击了这些元素,则不会再次关闭。

我发现这个插件,jALDropdown,即使我点击e.stopPropagation();,甚至点击页面或浏览器之外的某个地方,以某种方式处理关闭下拉列表,它似乎没有使用任何文档/正文-event-listener:http://india.assigninfo.com/assignlabs/jaldropdown

可悲的是,它不能与Opera合作,所以我无法使用它。而且我没有找到未最小化的源代码版本,因此我无法找到它的工作原理。

您是否有任何想法如何实现此行为,以便在没有文档/正文事件监听器的情况下单击元素,页面甚至浏览器外部时,下拉关闭(元素被隐藏)?谢谢!

2 个答案:

答案 0 :(得分:1)

为什么不保留$(document).click()函数来隐藏你的除法,然后将这个函数添加到你的代码中并调用它而不是event.stopPropagation():

function stopPropAndCloseDiv2(event) {
    $('#div2').hide();
    event.stopPropagation();
};

我测试了它,似乎它可以正常工作。你只需记得使用它。

更新:

这对我嗤之以鼻,我在这个知道的网站上找到了另一个答案。这是链接:

add code to a function programatically with JS

我尝试了这种方法,似乎有效:http://jsfiddle.net/kXkFS/8/

这是代码(测试页面标记中有两个按钮):

jQuery.Event.prototype.stopPropagation = (function() {
    var cached_function = jQuery.Event.prototype.stopPropagation;

    return function() { 
        if ( this.type == "click") {
            // Your logic here.
            console.log("I am a modified version of event.stopPropagation()!");
        }
        cached_function.apply(this, arguments); // use .apply() to call it.
    };
}());

$(document).click( function() { console.log("The document caught the event, too."); });
$('#clicker').click( function() { console.log("I'm going to let this event bubble."); });
$('#anotherClicker').click( function(event) { event.stopPropagation(); });

Spoiler:当您单击“clicker”按钮时,文档也会记录到控制台。单击“anotherClicker”按钮时,自定义event.stopPropagation()函数将记录到控制台,文档不会登录到控制台。现在,您可以将单击事件处理程序添加到文档中。

有一点:这可能会增加一些开销,但只有当你经常调用stopPropagation()时才会这样做。

如果有人想要像这样强调与jQuery对象搞砸的智慧,请这样做。我想知道,但我看不出像这样一个无害的变化会在将来炸毁网页。

答案 1 :(得分:0)

//generic drop-down menu
//NOTE: only pass a menu object if there are multiple menus per toggle object
var dropDown = function( toggle, menus )
{
  toggle.click( function( e ) {
    e.preventDefault();
    //NOTE: container is assuming that the next sibling is the dropdown.
    //change container to suit your needs
    var container = $( this ).next();
    if( menus !== undefined && !container.is( ':visible' ) ){ menus.hide(); }

    if( !container.is( ':visible' ) )
    {
      container.fadeIn( 'fast' );
    }
    else
    {
      container.fadeOut( 'fast' );
    }
  });
}

//close stuff when the window is clicked
var window_click = function( obj )
{
  $( window ).click( function( e ) {
    if( obj.is( ':visible' ) )
    {
      obj.fadeOut( 250 );
    }
  });
}

//override the window_click function when needed
var stop_prop = function( obj )
{
  obj.click( function( e ) {
    e.stopPropagation();
  });
}

现在,只需在任何地方使用它们:

var toggle = $( '#myToggle' );
var menu = $( '#myMenu' );

dropDown( toggle, menu );
window_click( menu );
stop_prop( toggle );
stop_prop( menu );

对于我的下拉标记,我几乎总是使用这个模型:

<span class='toggle_name'></span>
<ul class='menu_name' style='display:none;'>
  <li>cool man</li>
</ul>      

要绕过分散的stopPropagation(),您还可以创建一个手动关闭所有菜单类的小函数。超级简单:

var menus = {
  //add any dropdown menu ids or classes here
}

//run this method on any conflicting stopProp clicks
var closeMenus = function()
{
  for( var key in menus )
  {
    if( menus.hasOwnProperty( key ) )
    {
      menus[key].hide();
    }
  }
}