5

Oh man, I've been tearing my hair out over this. 4 hours on a dropdown.

I'm using Twitter Bootstrap.

The fixed nav at the top has a dropdown, pretty standard stuff.

Except that the dropdown doesn't get closed as it normally should. It will only toggle open and closed when the toggle itself is pressed, not when an item in the menu is pressed or the user clicks outside the menu (both of these should close the dropdown).

The only thing I'm doing non-standard is my use of an iframe and a theme from bootswatch.

I haven't had an issue like this before so I have a feeling it may be a bug (upgraded bootstrap to 2.1.0, and jquery to 1.7.2 today).

all the code here:

    <!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>test</title>
        <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
        <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]-->
        <!-- Le styles -->
        <link href="./css/bootstrap.css" rel="stylesheet">
        <style>
            iframe {
                border: 0px;
                height: 95%;
                left: 0px;
                position: absolute;
                top: 50px;
                width: 100%;
            }
        </style>
    </head>
    <body>
        <div class="navbar navbar-fixed-top">
            <div class="navbar-inner">
                <div class="container">
                    <a class="brand" target="mainframe" href="./home.html">
                       Brand
                    </a>
                    <ul class="nav">
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" role="button" data-toggle="dropdown">
                                <i class="icon-pencil icon-white"></i>
                                Sample
                                <b class="caret"></b>
                            </a>
                            <ul class="dropdown-menu" role="menu">
                                <li>
                                    <a target="mainframe" href="./home.html#">One</a>
                                </li>
                                <li>
                                    <a target="mainframe" href="./home.html#">Two</a>
                                </li>
                                <li>
                                    <a target="mainframe" href="./home.html#">Three</a>
                                </li>
                                <li>
                                    <a target="_blank" href="#">Four
                                        <i class="icon-share-alt"></i>
                                    </a>
                                </li>
                                <li>
                                    <a target="_blank" href="#">Five
                                        <i class="icon-share-alt"></i>
                                    </a>
                                </li>
                            </ul>
                        </li>
                        <li>
                            <a href="#">
                                <i class="icon-certificate icon-white"></i>Stuff</a></li>
                        <li>
                            <a href="#">
                                <i class="icon-globe icon-white"></i>Things</a></li>
                        <li>
                            <a target="mainframe" href="./home.html">
                                <i class="icon-film icon-white"></i>Nothing</a></li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="container">
            <iframe id="frame" name="mainframe" src="./home.html"></iframe>
            <!-- /container -->
        </div>
        <!-- Le javascript==================================================-->
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>

        <script src="./js/bootstrap.js"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                $('.dropdown-toggle').dropdown();
                var frameheight = window.innerHeight - 50;
                document.getElementById("frame").style.height = frameheight + "px";
            });

            $(window).resize(function () {
                var frameheight = window.innerHeight - 50;
                document.getElementById("frame").style.height = frameheight + "px";
            });
        </script>
    </body>
</html>

live here: http://www.joshlevent.com/dropdownbug/

Sameera Thilakasiri
  • 9,452
  • 10
  • 51
  • 86
Josh
  • 83
  • 1
  • 1
  • 8

8 Answers8

6

iframes don't propagate click events up to their parents by default. You need to add code to do that manually. In addition to being able to edit the content of the iframe you are loading, cross-domain restrictions will require you to be loading the iframe content from the same domain as your page.

This would need to run from within the iframe page:

$('html').on('click', function () {
  parent.$('#frame').trigger('click');
});

where #frame is the id of your iframe. You could delegate the click to anything in the parent (e.g., 'body') and that would work as well. Depends on how tightly you want to couple the content in the iframe to the page.

So, that should take care of the page closing the menu.

Important Update

As for clicking on the menu items to close the menu, apparently there was a bug[?] in the lastest update (2.0.4 > 2.1.0): Dropdown menus don't get closed when selecting an option. A selector was changed from '.dropdown form' to '.dropdown', so now what was once a celebrated feature to make working with dropdowns in forms easier has evolved into a callback that blocks all clicks on dropdown menu items.

An interim solution is to include the following code until the bug is fixed (2.1.1):

$('body')
  .off('click.dropdown touchstart.dropdown.data-api', '.dropdown')
  .on('click.dropdown touchstart.dropdown.data-api'
    , '.dropdown form'
    , function (e) { e.stopPropagation() })
merv
  • 67,214
  • 13
  • 180
  • 245
  • 1
    And even that doesn't seem to work on cross-domain iframes. Check http://stackoverflow.com/a/5083086/1478467 – Sherbrow Aug 25 '12 at 19:11
  • @Sherbrow Yep, you're right! I added a little note on the need to have same origin. – merv Aug 25 '12 at 19:40
  • perfect. Thanks for the pointers. I was able to solve the whole problem with this. Will post the solved HTML as a new answer below. – Josh Aug 26 '12 at 00:43
  • 1
    @Josh I did a bit more research and updated my answer. I think what I've added is a far better solution than switching to the button markup, since it will allow you to easily migrate your code to future versions without having to refactor your markup. – merv Aug 26 '12 at 05:24
4

Nothing worked for me, except this:

$('.dropdown-menu li a').on('click', function(e) {
    $('.dropdown-toggle').dropdown('toggle');
    e.stopPropagation();
});
2

I was seeing the same problem with 2.1.1.

The solution that was posted on GitHub which worked easily for me was:

$('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stopPropagation(); });

Credit to blakeembrey at https://github.com/twitter/bootstrap/issues/5094

Ivan Town
  • 445
  • 8
  • 11
1

This is the full solution I implemented based on merv's great answer. If there is a better way for me to post the solution for those coming in the future please let me know.

This jquery was added to the bottom of the html of the pages in the frame:

$('html').on('click', function () {
  parent.$('#frame').trigger('click');
});

The dropdown was switched to a button and restyled to fit into the navbar. In the html:

                <li class="btn-group" id="samplebtn">
                    <a href="#" class="btn btn-primary dropdown-toggle" role="button" data-toggle="dropdown">
                        <i class="icon-pencil icon-white"></i>
                        Sample
                        <b class="caret"></b>
                    </a>

In the CSS:

        #samplebtn {
        position:absolute;
        top: 0;
        margin: 0px;
        padding: 0px;
        border: 0px;
        }
        #samplebtn .btn {
            border: 0px;
        }

full code of solution (update of the full code in the question - this is the outside frame only):

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>test</title>
        <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
        <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]-->
        <!-- Le styles -->
        <link href="./css/bootstrap.css" rel="stylesheet">
        <style>
            iframe {
                border: 0px;
                height: 95%;
                left: 0px;
                position: absolute;
                top: 50px;
                width: 100%;
            }
            #samplebtn {
            position:absolute;
            top: 0;
            margin: 0px;
            padding: 0px;
            border: 0px;
            }
            #samplebtn .btn {
                border: 0px;
            }
        </style>
    </head>
    <body>
        <div class="navbar navbar-fixed-top">
            <div class="navbar-inner">
                <div class="container">
                    <a class="brand" target="mainframe" href="./home.html">
                       Brand
                    </a>
                    <ul class="nav">

                        <li class="btn-group" id="samplebtn">
                            <a href="#" class="btn btn-primary dropdown-toggle" role="button" data-toggle="dropdown">
                                <i class="icon-pencil icon-white"></i>
                                Sample
                                <b class="caret"></b>
                            </a>
                            <ul class="dropdown-menu" role="menu">
                                <li>
                                    <a target="mainframe" href="./home.html#">One</a>
                                </li>
                                <li>
                                    <a target="mainframe" href="./home.html#">Two</a>
                                </li>
                                <li>
                                    <a target="mainframe" href="./home.html#">Three</a>
                                </li>
                                <li>
                                    <a target="_blank" href="#">Four
                                        <i class="icon-share-alt"></i>
                                    </a>
                                </li>
                                <li>
                                    <a target="_blank" href="#">Five
                                        <i class="icon-share-alt"></i>
                                    </a>
                                </li>
                            </ul>
                        </li>
                        <li>
                            <a href="#">
                                <i class="icon-certificate icon-white"></i>Stuff</a></li>
                        <li>
                            <a href="#">
                                <i class="icon-globe icon-white"></i>Things</a></li>
                        <li>
                            <a target="mainframe" href="./home.html">
                                <i class="icon-film icon-white"></i>Nothing</a></li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="container">
            <iframe id="frame" name="mainframe" src="./home.html"></iframe>
            <!-- /container -->
        </div>
        <!-- Le javascript==================================================-->
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>

        <script src="./js/bootstrap.js"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                $('.dropdown-toggle').dropdown();
                var frameheight = window.innerHeight - 50;
                document.getElementById("frame").style.height = frameheight + "px";
            });

            $(window).resize(function () {
                var frameheight = window.innerHeight - 50;
                document.getElementById("frame").style.height = frameheight + "px";
            });

            $('.dropdown-menu').on('click', function () {
              $('.dropdown-toggle').dropdown();
            });
        </script>
    </body>
</html>

for complete reference, this is the code of a page inside the frame:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="utf-8">
        <title>Title</title>

        <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
        <!--[if lt IE 9]>
            <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
        <!-- Le styles -->
        <link href="./bootstrap.min.css" rel="stylesheet">
        <style>

        </style>
    </head>

    <body onload="scrollTo(0,0); $('#loading').css('display','none');">
        <div id="loading" style="width: 100%; height: 100%; background-color: white; background-image:url('loader.gif'); background-repeat:no-repeat; background-position: center center; position:fixed; display: none; z-index:100;"></div>
        <script type="text/javascript">
            document.getElementById("loading").style.display = 'block';
        </script>


        <div class="container">
        <div class="span12">
                    <h2>Project Description</h2>
                    <p>Lorem Ipsum Dolor Sit Amet</p>


                </div>

            <!-- /container -->
        </div>
        <!-- Le javascript==================================================-->

        <script src="./jquery.js"></script>
        <script src="./bootstrap.min.js"></script>
<!--        <script src="./bootstrap-dropdown.js"></script>
-->
<script>
$('html').on('click', function () {
  parent.$('#frame').trigger('click');
});

</script>

    </body>

</html>

Fixed testing site you can preview at (but don't copy the whole page code, as it has some server side (cloudflare) stuff in it). http://www.joshlevent.com/dropdownbug/

Josh
  • 83
  • 1
  • 1
  • 8
1

The bug is fixed now, You can use https://github.com/twitter/bootstrap/blob/2.1.1-wip/js/bootstrap-dropdown.js , the 2.1.1-wip for dropdown

Hyder
  • 1,463
  • 9
  • 14
1

you can remove the "open" class from dropdown menus when another is clicked. this worked for me so far:

$(document).ready(function () {
    $('a.dropdown-toggle').each(function(){
        $(this).on("click", function () {
            $('li.dropdown').each(function () {
                $(this).removeClass('open');
            });
        });
    });
});
detay
  • 1,082
  • 11
  • 19
1

My fix for more than one dropdowns in nav menu: Other solutions doesn't work for me

$('.dropdown-toggle').on('click', function (e) {
      $('.dropdown-toggle').each(function (i,elem) {
          if (elem != e.target) $(elem.parentElement).removeClass('open');
      });
})
Tomino
  • 5,969
  • 6
  • 38
  • 50
-1

You can also add propagate click event from the parent page with this code (fixed the same issue for me):

var myFrameContents = document.getElementById("frame").contentWindow.document;
myFrameContents.body.addEventListener("click", function() {
    document.body.click();
});
  • Just because you *can* use vanilla JS to accomplish the same thing doesn't mean you *should*, especially when jQuery is already loaded (as per OP). – merv Aug 23 '17 at 15:26