javascripthtmlcsstwitter-bootstrap

Wrap overflowing menu items in dropdown if there's not enough space


I have a Bootstrap navbar menu, and I'm trying to wrap overflowing menu items in a dropdown if there's not enough avaliable space. I have this solution - it works perfectly and does what I'm looking for:

const contentBar = document.querySelector(".context-bar")
const dropdown = document.querySelector(".grouped-content")

const update = () => {
  const offsetTop = contentBar.offsetTop
  dropdown.innerHTML = ""

  document.querySelectorAll(".context-bar > a").forEach((item) => {
    if (item.offsetTop > offsetTop) {
      let li = document.createElement("li")
      li.innerHTML += item.outerHTML
      dropdown.appendChild(li)
    }
  })
}

update()
window.addEventListener("resize", update)
.link-bar {
  display: flex;
  position: relative;
  background: #ddd;
  padding: 0 10px;
  color: #000;
  width: 100%;
  box-sizing: border-box;
}

.context-bar {
  flex: 1;
  height: 60px;
  overflow: hidden;
}

.context-bar-link, .grouped-link {
  color: #000;
  display: inline-block;
  white-space: nowrap;
  max-width: 18rem;
  text-overflow: ellipsis;
  overflow: hidden;
  padding: 20px 10px;
}

.grouped-content {
    display: none;
    list-style-type: none;
    position: absolute;
    background-color: #f9f9f9;
    min-width: 160px;
    right: 10px;
    max-height: 200px;
    overflow-y: auto;
}

.grouped:hover .grouped-content {
 display: block;
}

body {
  padding-top: 50px;
}


a {
  text-decoration: none;
}
<div class="container">
  <nav class="link-bar">
    <div class="context-bar">
      <a href="#" class="context-bar-link">Item 1</a>
      <a href="#" class="context-bar-link">Item 22</a>
      <a href="#" class="context-bar-link">Item 333</a>
      <a href="#" class="context-bar-link">Item 4444</a>
      <a href="#" class="context-bar-link">Item 5555555</a>
      <a href="#" class="context-bar-link">Item 66</a>
      <a href="#" class="context-bar-link">Item 7777777777777</a>
      <a href="#" class="context-bar-link">Item 8</a>
      <a href="#" class="context-bar-link">Item 999999</a>
      <a href="#" class="context-bar-link">Item 10101010</a>
      <a href="#" class="context-bar-link">Item 1111</a>
      <a href="#" class="context-bar-link">Item 12121212</a>
      <a href="#" class="context-bar-link">Item 1313131313131</a>
      <a href="#" class="context-bar-link">Item 1414</a>
      <a href="#" class="context-bar-link">Item 15</a>
      <a href="#" class="context-bar-link">Item 16161616</a>
      <a href="#" class="context-bar-link">Item 1717</a>
      <a href="#" class="context-bar-link">Item 18181818181818</a>
      <a href="#" class="context-bar-link">Item 191919</a>
      <a href="#" class="context-bar-link">Item 20</a>
      <a href="#" class="context-bar-link">Item 212121</a>
      <a href="#" class="context-bar-link">Item 2222</a>
    </div>
    <div class="grouped">
      <a href="#" class="grouped-link">More</a>
      <ul class="grouped-content"></ul>
    </div>
  </nav>
</div>

However, it doesn't woth with Bootstrap's navbar - probably because we can't check for offsetTop? How can I change the script to work with the Bootstrap navbar?

This is what I have:

const contentBar = document.querySelector(".context-bar")
const dropdown = document.querySelector(".grouped-content")

const update = () => {
  const offsetTop = contentBar.offsetTop
  dropdown.innerHTML = ""

  document.querySelectorAll(".context-bar > li").forEach((item) => {
    if (item.offsetTop > offsetTop) {
      let li = document.createElement("li")
      li.innerHTML += item.outerHTML
      dropdown.appendChild(li)
    }
  })
}

update()
window.addEventListener("resize", update)
.context-bar {
  flex: 1;
  height: 60px;
  overflow: hidden;
}

.nav-link,
.grouped-link {
  color: #000;
  display: inline-block;
  white-space: nowrap;
  max-width: 18rem;
  text-overflow: ellipsis;
  overflow: hidden;
  padding: 20px 10px;
}

.grouped-content {
  display: none;
  list-style-type: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  right: 10px;
  max-height: 200px;
  overflow-y: auto;
}

.grouped:hover .grouped-content {
  display: block;
}

body {
  padding-top: 50px;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container">
<nav class="navbar navbar-expand-sm link-bar">
  <ul class="navbar-nav context-bar">
    <li class="nav-item"><a href="#" class="nav-link">Item 1</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 22</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 333</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 4444</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 5555555</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 66</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 7777777777777</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 8</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 999999</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 10101010</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 1111</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 12121212</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 1313131313131</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 1414</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 15</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 16161616</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 1717</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 18181818181818</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 191919</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 20</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 212121</a></li>
    <li class="nav-item"><a href="#" class="nav-link">Item 2222</a></li>
    <li class="grouped">
      <a href="#" class="grouped-link">More</a>
      <ul class="grouped-content"></ul>
    </li>
  </ul>
</nav>
</div>
How can I modify the script to work with the styling from Bootstrap? Anyone with a good idea? Thank you.


Solution

  • You need to active the wrapping and the "grouped" element need to be outside the list.

    const contentBar = document.querySelector(".context-bar")
    const dropdown = document.querySelector(".grouped-content")
    
    const update = () => {
      const offsetTop = contentBar.offsetTop
      dropdown.innerHTML = ""
    
      document.querySelectorAll(".context-bar > li").forEach((item) => {
        if (item.offsetTop > offsetTop) {
          let li = document.createElement("li")
          li.innerHTML += item.outerHTML
          dropdown.appendChild(li)
        }
      })
    }
    
    update()
    window.addEventListener("resize", update)
    .context-bar {
      flex: 1;
      height: 40px; /* not 60px */
      overflow: hidden;
      flex-wrap: wrap; /* added this*/
    }
    
    .nav-link,
    .grouped-link {
      color: #000;
      display: inline-block;
      white-space: nowrap;
      max-width: 18rem;
      text-overflow: ellipsis;
      overflow: hidden;
      padding: 20px 10px;
    }
    
    .grouped-content {
      display: none;
      list-style-type: none;
      position: absolute;
      background-color: #f9f9f9;
      min-width: 160px;
      right: 10px;
      max-height: 200px;
      overflow-y: auto;
    }
    
    .grouped:hover .grouped-content {
      display: block;
    }
    
    body {
      padding-top: 50px;
    }
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"/>
    <div class="container">
    <nav class="navbar navbar-expand-sm link-bar">
      <ul class="navbar-nav context-bar">
        <li class="nav-item"><a href="#" class="nav-link">Item 1</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 22</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 333</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 4444</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 5555555</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 66</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 7777777777777</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 8</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 999999</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 10101010</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 1111</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 12121212</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 1313131313131</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 1414</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 15</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 16161616</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 1717</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 18181818181818</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 191919</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 20</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 212121</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Item 2222</a></li>
      </ul>
        <div class="grouped">
          <a href="#" class="grouped-link">More</a>
          <ul class="grouped-content"></ul>
        </div>
    </nav>
    </div>