I've got your typical dropdown navigation, and I'm trying to make sure the drop menu links are always accessible and visible:
<li><a href="#">Link 1</a> <ul> <li><a href="#">Link 1</a></li> <li><a href="#">Link 2</a></li> <li><a href="#">Link 3</a></li> </ul> </li> <li><a href="#">Link 2</a> <ul> <li><a href="#">Link 1</a></li> <li><a href="#">Link 2</a></li> <li><a href="#">Link 3</a></li> </ul> </li> <!-- etc. --> </ul>
The CSS really isn't anything special (colors and backgrounds removed):
.dropdown, .dropdown li, .dropdown ul { list-style:none; margin:0; padding:0; } .dropdown { position:relative; z-index:10000; float:left; width:100%; } .dropdown ul { position:absolute; top:100%; visibility:hidden; display:none; width:16em; } .dropdown ul ul { top:0; left:100%; } .dropdown li { position:relative; float:left; } .dropdown li:hover{ z-index:910; } .dropdown ul:hover, .dropdown li:hover > ul, .dropdown a:hover + ul, .dropdown a:focus + ul { visibility:visible; display:block; } .dropdown a { display:block; padding:1em 2em; } .dropdown ul li { width:100%; }
There are an unknown number of top level links (they are created by the user). The problem I'm having is that sometimes the drop menus (which go to the right) will go off screen if the top level link is too far to the right. I added this bit of CSS to compensate:
.dropdown > li:last-child ul { /* ...or use a class on the last link for IE */ right:0; }
Now the last one goes to the left instead of off screen, which is nice, but there are a few issues:
Resize the panel in this demo to see what I mean (the red area is considered "off screen") http://jsfiddle.net/G7qfq/
I've struggled with this annoyingly common problem for years and have never found a satisfactory solution. Is there any way to check if the drop menu would go off screen, and if so, add/remove a class
name or something so I can keep it on screen with CSS?
One clue I might use is that if a menu does go off screen, it always produces a vertical scroll bar at the bottom of the window, but I'm not sure how to use that knowledge. I tried the accepted answer to this question about detecting vertical scroll bars, but for some reason it always returns true
, and always adds the "edge" class (maybe there's an issue with the timing?):
$(".dropdown li").on('mouseenter mouseleave', function (e) { // Get the computed style of the body element var cStyle = document.body.currentStyle||window.getComputedStyle(document.body, ""); // Check the overflow and overflowY properties for "auto" and "visible" values hasVScroll = cStyle.overflow == "visible" || cStyle.overflowY == "visible" || (hasVScroll && cStyle.overflow == "auto") || (hasVScroll && cStyle.overflowY == "auto"); if (hasVScroll) { $(this).addClass('edge'); } else { $(this).removeClass('edge'); } });
Demo with the javascript: http://jsfiddle.net/G7qfq/2/
Really, I don't want to see a vertical scroll bar even for a split second so I'm not sure that's the way to go, plus there could be false positives (scroll bar for some other reason).
I also tried the solution in this answer which I admit, I don't quite understand, and couldn't get it to work: http://jsfiddle.net/G7qfq/3/
$(".dropdown li").on('mouseenter mouseleave', function (e) { var elm = $('ul:first', this); var off = elm .offset(); var t = off.top; var l = off.left; var h = elm.height(); var w = elm.width(); var docH = $(window).height(); var docW = $(window).width(); var isEntirelyVisible = (t > 0 && l > 0 && t + h < docH && l+ w < docW); if ( ! isEntirelyVisible ) { $(this).addClass('edge'); } else { $(this).removeClass('edge'); } });
I assume the solution requires javascript, and I am using jQuery, but I haven't got a clue how to approach the problem. Any ideas?
To open the dropdown menu, use a button or a link with a class of . dropdown-toggle and the data-toggle="dropdown" attribute. The . caret class creates a caret arrow icon (), which indicates that the button is a dropdown.
Wrap a <div> element around the elements to position the dropdown content correctly with CSS. CSS) The . dropdown class uses position:relative , which is needed when we want the dropdown content to be placed right below the dropdown button (using position:absolute ).
Example Explained Use any element to open the dropdown menu, e.g. a <button>, <a> or <p> element. Use a container element (like <div>) to create the dropdown menu and add the dropdown links inside it. Wrap a <div> element around the button and the <div> to position the dropdown menu correctly with CSS.
I think you were almost there...
You should really only be interested in the calculations involved in the width. If the width of the dropdown element and the offset of that element is greater than the width of the container, you want to switch your menu.
$(function () { $(".dropdown li").on('mouseenter mouseleave', function (e) { if ($('ul', this).length) { var elm = $('ul:first', this); var off = elm.offset(); var l = off.left; var w = elm.width(); var docH = $(".container").height(); var docW = $(".container").width(); var isEntirelyVisible = (l + w <= docW); if (!isEntirelyVisible) { $(this).addClass('edge'); } else { $(this).removeClass('edge'); } } }); });
http://jsfiddle.net/G7qfq/582/
Here is a function that can be used for menus that fly-out to the right, or down (based off @r0m4n's code):
function fixFlyout (containerElement, parentElement,flyoutElement,flyoutDirection) { $(parentElement).on('mouseenter mouseleave', function (e) { var element = $(flyoutElement, this); var offset = element .offset(); switch(flyoutDirection) { case 'down': var top = offset.top; var height = element.height(); var windowHeight = $(containerElement).height(); var isEntirelyVisible = (top + height <= windowHeight); break; case 'right': var left = offset.left; var width = element.width(); var windowWidth = $(containerElement).width(); var isEntirelyVisible = (left + width <= windowWidth); break; } if (!isEntirelyVisible ) { $(element).addClass('edge'); } else { $(element).removeClass('edge'); } }); } //Level 1 Flyout fixFlyout(containerElement = '.header',parentElement = '.header .navigation>.menu>.expanded',flyoutElement = '.menu:first',flyoutDirection = 'down');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With