Long sub menus with native scrollbar

by Vasil Dinkov

HTML

<script src="https://vadikom.github.io/smartmenus/src/jquery.smartmenus.js"></script>
<link rel="stylesheet" href="https://vadikom.github.io/smartmenus/src/css/sm-core-css.css">
<link rel="stylesheet" href="https://vadikom.github.io/smartmenus/src/css/sm-blue/sm-blue.css">
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<nav role="navigation">
  <!-- Sample menu definition -->
  <ul id="main-menu" class="sm sm-blue">
    <li><a href="http://www.smartmenus.org/">Home</a></li>
    <li><a href="#">Long sub 1</a>
      <ul>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">A pretty long text to test the default subMenusMaxWidth:20em setting for the sub menus</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">A pretty long text to test the default subMenusMaxWidth:20em setting for the sub menus</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">Dummy item</a></li>
        <li><a href="#">A pretty long text to test the default subMenusMaxWidth:20em setting for the sub menus</a></li>
        <li><a href="#">Dummy...

CSS

/* IGNORE: Unrelated generic demo styles */

body {
  margin: 8px;
  background: #fff;
  color: #aaa;
  font: 16px/1.5em 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

.demo-text {
  margin: 3em 22px;
}

.demo-text p {
  margin-bottom: 1em;
}

.demo-text a {
  color: #999;
}

JavaScript

// SmartMenus init
$(function() {
  $('#main-menu').smartmenus({
    subMenusSubOffsetX: 1,
    subMenusSubOffsetY: -8
  });
});

// Set proper max-height for sub menus in desktop view
$('#main-menu').bind('beforeshow.smapi', function(e, menu) {
  var $sub = $(menu),
    hasSubMenus = $sub.find('ul').length && !$sub.hasClass('mega-menu');
  // if the sub doesn't have any deeper sub menus, apply max-height
  if (!hasSubMenus) {
    var obj = $(this).data('smartmenus');
    if (obj.isCollapsible()) {
      $sub.css({
        'overflow-y': '',
        'max-height': ''
      });
    } else {
      var $a = $sub.dataSM('parent-a'),
        $li = $a.closest('li'),
        $ul = $li.parent(),
        level = $sub.dataSM('level'),
        $win = $(window),
        winH = $win.height(),
        winY = $win.scrollTop(),
        subY = winY;
      // if the parent menu is horizontal
      if ($ul.parent().is('[data-sm-horizontal-sub]') || level == 2 && !$ul.hasClass('sm-vertical')) {
        var itemY = $a.offset().top,
          itemH = obj.getHeight($a),
          subOffsetY = level == 2 ? obj.opts.mainMenuSubOffsetY : obj.opts.subMenusSubOffsetY,
          subY = itemY + itemH + subOffsetY;
      }
      $sub.css({
        'max-height': winH + winY - subY
      });
    }
  }
});

// Set overflow-y: auto for sub menus in desktop view
// this needs to be done on the 'show.smapi' event because the script resets overflow on menu show
$('#main-menu').bind('show.smapi', function(e, menu) {
  var $sub = $(menu),
    hasSubMenus = $sub.find('ul').length && !$sub.hasClass('mega-menu');
  // if the sub doesn't have any deeper sub menus, apply overflow-y: auto
  if (!hasSubMenus) {
    var obj = $(this).data('smartmenus');
    if (!obj.isCollapsible()) {
      // the timeout fixes an issue in jQuery 3
      // it may need to be adjusted if you use a longer "showDuration" than the default
      setTimeout(function() {
        $sub.css('overflow-y', 'auto');
      }, 1);
    }
  }
});