Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Position absolute dropdown in scrollable container

Is it possible to make the position absolute dropdown stay on top of the scrollable container, and move along with the position relative parent when scrolling? Right now, the dropdown appears within the scrollable area.

The screenshot below is what I want to achieve. I'm also open to javascript solutions if needed.

enter image description here

jsFiddle

.navbar {
  padding-left: 0;
  white-space: nowrap;
  overflow: auto;
}

.navbar-item {
  position: relative;
  margin-left: 0;
  list-style: none;
  display: inline-block;
  width: 200px;
  text-align: center;
  border: 1px solid;
}

.dropdown {
  position: absolute;
  padding-left: 0;
  width: 200px;
  box-shadow: 0 1px 0 1px black;
  display: none;
}

.dropdown-item {
  margin-left: 0;
  list-style: none;
}

.navbar-item:hover .dropdown {
  display: block;
}
<ul class="navbar">
  <li class="navbar-item">
    <a>Item A</a>
    <ul class="dropdown">
      <li class="dropdown-item">Sub Item A</li>
      <li class="dropdown-item">Sub Item B</li>
      <li class="dropdown-item">Sub Item C</li>
    </ul>
  </li>
  <li class="navbar-item"><a>Item B</a></li>
  <li class="navbar-item"><a>Item C</a></li>
  <li class="navbar-item"><a>Item D</a></li>
  <li class="navbar-item"><a>Item E</a></li>
</ul>
like image 770
Stickers Avatar asked Jan 09 '18 15:01

Stickers


2 Answers

An easy way is to make the position of dropdown fixed, but make sure to apply the left and top values accordingly. Another way is to append the dropdown menu outside the ul element, so when it appears it will not be wrapped within it.

.dropdown {
  position: fixed;
}

.navbar {
  padding-left: 0;
  white-space: nowrap;
  overflow: auto;
}

.navbar-item {
  position: relative;
  margin-left: 0;
  list-style: none;
  display: inline-block;
  width: 200px;
  text-align: center;
  border: 1px solid;
}

.dropdown {
  position: fixed;
  background-color: #fff;
  padding-left: 0;
  width: 200px;
  box-shadow: 0 1px 0 1px black;
  display: none;
}

.navbar-item {
  margin-left: 0;
  list-style: none;
}

.navbar-item:hover .dropdown {
  display: block;
}
<ul class="navbar">
  <li class="navbar-item">
    <a>Item A</a>
    <ul class="dropdown">
      <li class="dropdown-item">Sub Item A</li>
      <li class="dropdown-item">Sub Item B</li>
      <li class="dropdown-item">Sub Item C</li>
    </ul>
  </li>
  <li class="navbar-item"><a>Item B</a></li>
  <li class="navbar-item"><a>Item C</a></li>
  <li class="navbar-item"><a>Item D</a></li>
  <li class="navbar-item"><a>Item E</a></li>
</ul>

Edit

I added one more snippet that does not use fixed position. It changes its coordinates when open based on the parent offset.

$('#menu1, #submenu1').mouseover(function(event) {
  $('#submenu1').addClass("show").css($('#menu1').offset());
});

$('#menu1, #submenu1').mouseleave(function(event) {
  $('#submenu1').removeClass("show");
});
.navbar {
  padding-left: 0;
  white-space: nowrap;
  overflow: auto;
}

.navbar-item {
  position: relative;
  margin-left: 0;
  list-style: none;
  display: inline-block;
  width: 200px;
  text-align: center;
  border: 1px solid;
}

.dropdown {
  position: absolute;
  padding-left: 0;
  width: 200px;
  box-shadow: 0 1px 0 1px black;
  display: none;
}

.dropdown.show {
  display: block;
  margin-left: 1px;
  background-color: #fff;
}

.dropdown-item {
  margin-left: 0;
  list-style: none;
}

.navbar-item:hover .dropdown {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="navbar">
  <li id="menu1" class="navbar-item">
    <a>Item A</a>
  </li>
  <li class="navbar-item"><a>Item B</a></li>
  <li class="navbar-item"><a>Item C</a></li>
  <li class="navbar-item"><a>Item D</a></li>
  <li class="navbar-item"><a>Item E</a></li>
</ul>

<ul id="submenu1" class="dropdown">
  <li class="dropdown-item">Sub Item A</li>
  <li class="dropdown-item">Sub Item B</li>
  <li class="dropdown-item">Sub Item C</li>
</ul>
like image 72
orabis Avatar answered Oct 15 '22 02:10

orabis


Here's an option using jQuery.

Basically, as others have mentioned, you'll have to move the subnav items outside of the main nav's wrapper, so that they are not hidden.

We can put them in their own wrapper that has a similar markup to the main nav's wrapper, and then match them up using a data element to allow for showing/hiding on click.

Then, when we scroll the main navbar, we can sync the subnav wrapper to that element's scroll position, so the subnav items appear to be fixed to the main nav items.

Lastly, we can hide the subnav wrapper's scroll bar with CSS.

The snippet below should get you the general idea, then you can style it as necessary to make it look more like the primary and secondary nav items are connected.

Click a nav item in the main nav, then scroll to the side to see it in action:

$(function(){
	var navbar = $('.navbar');
  var subnavBar = $('.subnavs-wrapper .scrollbar-hider');

  $(navbar).find('li').each(function(){
    var dataId = $(this).attr('data-id');
    var matchingUl = $(this).parent().next().find('ul[data-id="' + dataId + '"]');
    $(this).on('click', function(){
      $(matchingUl).css('visibility') == 'hidden' ?
      $(matchingUl).css('visibility', 'visible') : $(matchingUl).css('visibility', 'hidden');
    });
    
		$(navbar).on('scroll', function(){
      $(subnavBar).scrollLeft($(this).scrollLeft());
    });
	});
});
.navbar {
  padding-left: 0;
  white-space: nowrap;
  overflow: auto;
  display: flex;
}

.navbar-item {
  position: relative;
  margin-left: 0;
  list-style: none;
  flex-shrink: 0;
  width: 200px;
  text-align: center;
  border: 1px solid;
}

.dropdown {
  position: absolute;
  padding-left: 0;
  width: 200px;
  box-shadow: 0 1px 0 1px black;
  display: none;
}

.navbar-item {
  margin-left: 0;
  list-style: none;
}

.navbar-item:hover .dropdown {
  display: block;
}

.subnavs-wrapper {
  overflow: hidden; /* hide scrollbar */
}

.subnavs-wrapper .scrollbar-hider {
  display: flex;
  overflow: auto;
  padding-bottom: 50px; /* hide scrollbar */
  margin-bottom: -50px; /* hide scrollbar */
}

.subnav {
  width: 200px;
  padding-left: 0;
  list-style: none;
  visibility: hidden;
  flex-shrink: 0;
  border: 1px solid black;
}
<ul class="navbar">
  <li class="navbar-item" data-id="one"><a>Item A</a>
  <li class="navbar-item" data-id="two"><a>Item B</a></li>
  <li class="navbar-item" data-id="three"><a>Item C</a></li>
  <li class="navbar-item" data-id="four"><a>Item D</a></li>
  <li class="navbar-item" data-id="five"><a>Item E</a></li>
</ul>
<div class="subnavs-wrapper">
  <div class="scrollbar-hider">
    <ul class="subnav" data-id="one">
      <li>Sub Item A.A</li>
      <li>Sub Item A.B</li>
      <li>Sub Item A.C</li>
    </ul>
    <ul class="subnav" data-id="two">
      <li><a>Sub Item B.A</a></li>
    </ul>
    <ul class="subnav" data-id="three">
      <li><a>Sub Item C.A</a></li>
      <li><a>Sub Item C.B</a></li>
    </ul>
    <ul class="subnav" data-id="four">
      <li><a>Sub Item D.A</a></li>
      <li><a>Sub Item D.B</a></li>
    </ul>
    <ul class="subnav" data-id="five">
      <li><a>Sub Item E.A</a></li>
      <li><a>Sub Item E.B</a></li>
    </ul>
  </div>
</div>

<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
like image 42
cjl750 Avatar answered Oct 15 '22 04:10

cjl750