【问题标题】:Popup menu using flexbox使用 flexbox 弹出菜单
【发布时间】:2017-01-02 18:15:20
【问题描述】:

我正在尝试使用 FlexBox 构建一个标头组件。这是我想要实现的目标的图片:

红色盒子是一个弹性盒子。绿色框是 flexbox 中的每个元素。最右边的元素(标记为 3)有一个与之连接的点击事件。这个想法是,当单击该框时,会立即在其下方弹出一个菜单,但不在其中。

我尝试使用 div 的 3 和 4 的绝对和相对定位,但没有任何组合可以达到我想要的效果。我能做的最好的事情是在 div 4 上使用绝对定位。它按预期从 div 3 中弹出,但 div 4 的宽度不会比 div 3 大,并且内容会换行。

如何在 div 4 中有一个弹出元素,它从自己的内容中获取宽度,并且不换行?

【问题讨论】:

  • 您可以发布您现在拥有的代码吗?

标签: html css flexbox


【解决方案1】:

假设您有一个像下面这样的简单标记结构,这很可能最好使用绝对定位的div 来实现

这将在parent 之外显示您的popup,对相同的宽度/高度没有限制

我在这里选择将 popup 放在 3:rd div 之外,因为它可以让您更灵活地根据响应能力(不同的屏幕尺寸等)放置它

基于脚本的版本

document.querySelector('.click').addEventListener('click', function(e){
  e.target.nextElementSibling.classList.toggle('clicked');
})
.parent {
  position: relative;
  display: flex;
  border: 1px solid red;
}
.parent div {
  flex: 2;
  border: 1px solid lime;
  margin: 1px;
}
.parent div:nth-child(2) {
  flex: 3;
}
.parent div:nth-child(3) {
  flex: 2;
}
.parent .popup {
  display: none;
  position: absolute;
  right: -2px;
  top: calc(100% + 3px);
  border: 1px solid blue;
}
.parent .popup.clicked {
  display: block;
}
<div class="parent">
  <div> 1 </div>
  <div> 2 </div>
  <div class="click"> 3 <br> (click to toggle) </div>
  <div class="popup"> This one can have text <br>
        that does pretty much what you want    
  </div>
</div>

这也可以在没有任何脚本的情况下完成,使用 labelcheckbox

更新

点击页面中的任意位置时popup 关闭(感谢I Love CSS

.parent {
  position: relative;
  display: flex;
  border: 1px solid red;
}
.parent div {
  flex: 2;
  border: 1px solid lime;
  margin: 1px;
}
.parent div:nth-child(2) {
  flex: 3;
}
.parent div:nth-child(3) {
  flex: 2;
}
.parent .popup {
  display: none;
  position: absolute;
  right: -2px;
  top: calc(100% + 3px);
  border: 1px solid blue;
}
.parent .modal {
  display: none;
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: white;
  opacity: 0.3;
}
.parent .click label[for=chkbox] {
  display: block;  
}
#chkbox {
  display: none;
}
#chkbox:checked ~ .modal,
#chkbox:checked ~ .popup {
  display: block;
}
<div class="parent">
  <div> 1 </div>
  <div> 2 </div>
  <div class="click"><label for="chkbox"> 3 <br> (click to toggle) </label></div>
  <input type="checkbox" id="chkbox">
  <label class="modal" for="chkbox"></label>
  <div class="popup"> This one can have text <br>
        that does pretty much what you want    
  </div>
</div>

一个没有任何脚本的版本,使用:focus

更新

使用:hover 使popup 保持不变,因此链接也可以工作(感谢Andrei Gheorghiu

.parent {
  position: relative;
  display: flex;
  border: 1px solid red;
}
.parent div {
  flex: 2;
  border: 1px solid lime;
  margin: 1px;
}
.parent div:nth-child(2) {
  flex: 3;
}
.parent div:nth-child(3) {
  flex: 2;
}
.parent .popup {
  display: none;
  position: absolute;
  right: -2px;
  top: calc(100% + 3px);
  border: 1px solid blue;
}
.click:focus + .popup {
  display: block;
}
.click + .popup:hover {
  display: block;
}
<div class="parent">
  <div> 1 </div>
  <div> 2 </div>
  <div class="click" tabindex="-1"> 3 <br> (click to toggle) </div>
  <div class="popup"> This one can have text <br>
        that does pretty much what you want <br><br>
        <a href="#" onclick="alert('hey');">links included</a>
  </div>
</div>

【讨论】:

  • pure css checkbox 切换技巧的问题是在下拉菜单外单击/点击时无法关闭它。这就是当今移动网络的预期行为。少于1% 的用户禁用了javascript,但远多于50% 的用户希望在下拉菜单之外单击/点击时会关闭。
  • @AndreiGheorghiu 我不介意有人质疑我的答案,相反,这让我思考......我同意人们在走出盒子时应该小心...您也可以选择东西,包括链接,更新 3:rd 示例以显示:) ... 也祝您新年快乐
  • @ILoveCSS 感谢您的评论/链接...更新了我的第二个示例
  • @ILoveCSS 现在我的 2:nd 示例与您的链接相同......不过代码更少:)
  • @ILoveCSS,我是第一个投票支持它的人,但它不像现在那么完整;)我实际上认为我的 cmets 让 LGSon 在这方面付出了比他最初更多的努力着手。但当然,我可能是错的。我只是在这里猜测......
【解决方案2】:

编辑 2: 使用此设置,您无需在下拉列表中硬编码任何 top 值,因此获得了灵活性并保持与导航栏的对齐(即使在不同视口宽度上调整大小时)。

$(".item-red").on("click", function() {
  $(".item-dropdown").toggleClass("display");
});
.container {
    display: flex;
    flex-flow: row wrap;
    text-align: center;
}
.item {
    flex: 2;
    padding: 5px;
    margin: 0;
}
.item-blue {
    background: lightblue;
}

.item-green {
    background: lightgreen;
    flex: 3;
}
.item-red {
    background: lightgray;
    flex: 1;
}
.item-red:hover {
    cursor: pointer;
}
.container-dropdown {
    display: flex;
    flex-flow: row wrap;
    position: relative;
    text-align: center;
}
.item-dropdown {
    width: 240px;
    display: none;
    background: gold;
    position: absolute;
    right: 0;
    z-index: 1;
    padding: 5px;
}
.display {
    display: flex;
}
.content {
    text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
    <div class="item item-blue">Lorem</div>
    <div class="item item-green">Lorem ipsum dolor kjghj</div>
    <div class="item item-red">Lorem ipsum dolor
    </div>
</div>
<div class="container-dropdown">
    <div class="item-dropdown">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga voluptate, ipsum consequuntur maiores unde laboriosam suscipit velit corporis.</div>
</div>
<div class="content">Content - lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam delen iti soluta qui, incidunt, neque est doloribus esse deserunt modi, mollitia delectus illum! Ullam nihil reiciendis animi eligendi nemo non. Incidunt.</div>

【讨论】:

    【解决方案3】:

    这是下拉菜单的正常行为,“菜单栏”项的显示方式(弹性、框或表格)并不重要。条件是:

    • 父母有position:relative;
    • 孩子有position:absolute; top: 100%;
    • 您是否希望孩子在 hover 上可见,或者父母是否应用了某个类,这是 UI/UX 和个人选择的问题。

    使用负边距,您可以将下拉菜单设置为大于父级。或者,您甚至可以将它们设为整页大小并像超级菜单一样(记住它们是绝对定位的)。

    这是一个例子:

    body {
      margin: 0;
      padding: 0;
      background-color: #f5f5f5;
    }
    
    .flex-menu {
      display: flex;
      background-color: white;
    }
    .flex-menu > * {
      position: relative;
      flex: 1 0 auto;
      padding: 10px;
      border-right: 1px solid #eee;
      cursor: pointer;
    }
    .flex-menu > *:last-child {
      border-right: none;
    }
    .flex-menu > * .submenu {
      position: absolute;
      top: 100%;
      left: 0;
      display: none;
      background-color: white;
      border-top: 1px solid #eee;
      padding: 10px;
      box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12)
    }
    .flex-menu .has-megamenu {
      position: static
    }
    .flex-menu > * .submenu.megamenu {
      width: 100vw;
      left: 0;
      top: 39px;
      box-sizing: border-box;
    }
    .flex-menu > *:hover .submenu {
      display: block;
    }
    
    .flex-menu > *:last-child .submenu {
      right: 0;
      margin-left: -100%;
    }
    <div class="flex-menu">
      <div>first item</div>
      <div>second item
        <div class="submenu">
          This is a dropdopwn content.
        </div>
      </div>
      <div class="has-megamenu">third item
        <div class="submenu megamenu">
          This is a mega menu dropdopwn content. You can put anything here. A full page, of content, if you want
        </div>
      </div>
      <div>fourth item
        <div class="submenu">
          This is a dropdopwn content.
        </div>
      </div>
    </div>

    至于您的要求:

    从它自己的内容中获取宽度,并且不换行

    通常,您会创建一个具有透明背景的“超级菜单”(全内容宽度)子项,并且您可以使用float:rightflex 将内容放置为此超级菜单的子项。我假设您希望它在达到完整内容宽度时最终换行。示例:

    var closeDrops = function(e) {
      $('.flex-menu div').removeClass('active');
    }
    $('body').on('click', closeDrops);
    
    $('.flex-menu div').on('click', function(e){
      if (!$(this).hasClass('active')) {
        closeDrops();
      }
      e.stopPropagation();
      $(this).toggleClass('active');
    })
    body {
      margin: 0;
      padding: 0;
      background-color: #f5f5f5;
      min-height: 100vh;
      font-family: sans-serif;
    }
    
    .flex-menu {
      display: flex;
      position: relative;
    }
    .flex-menu > * {
      position: relative;
      flex: 1 0 auto;
      padding: 10px;
      border: solid white;
      border-width: 0 1px 1px 0;  
      cursor: pointer;
      transition: background-color .3s ease-in-out;
    }
    .flex-menu > *:last-child {
      border-right: none;
    }
    .flex-menu > * .submenu {
      position: absolute;
      top: 100%;
      left: 0;
      display: none;
      background-color: white;
      border-top: 1px solid #eee;
      padding: 10px;
      box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12)
    }
    .flex-menu .has-megamenu {
      position: static
    }
    .flex-menu > * .submenu.megamenu {
      width: 100vw;
      left: 0;
      box-sizing: border-box;
    }
    .flex-menu > *:hover .submenu,.flex-menu > *.active .submenu  {
      display: block;
    }
    .flex-menu > *.active, .flex-menu > *:hover {
      box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12);
      background-color: white;
    }
    .flex-menu > *.active{
      z-index: 1;
      
    }
    .flex-menu > *:hover {
      z-index: 2;
    }
    .has-megamenu:last-child .megamenu{
      background-color: transparent; padding: 0;
      box-shadow: none;
    }
    .has-megamenu:last-child .megamenu > * {
      box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12);
    }
    .placed-right {
      background-color: white;
      padding:10px;
      float: right;
    }
    @media (max-width: 500px) {
      .flex-menu {
        flex-direction: column;
      }
      .flex-menu > * .submenu {
        width: 100vw;
        box-sizing: border-box;
      }
      .placed-right { 
        float: none;
        width: 100%;
        display: block;
        box-sizing: border-box;
      }
      .flex-menu .has-megamenu {
        position: relative;
      }
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="flex-menu">
      <div>first item</div>
      <div>second item
        <div class="submenu">
          This is a dropdopwn content.
        </div>
      </div>
      <div class="has-megamenu">third item
        <div class="submenu megamenu">
          This is a mega menu dropdopwn content. You can put anything here. A full page, of content, if you want
        </div>
      </div>
      <div class="has-megamenu">fourth item
        <div class="submenu megamenu">
          <span class="placed-right">
            I am right-aligned and I don't care about my parent's width, ok? <hr />I'll only wrap when I don't fit in page.
          </span>
        </div>
      </div>
    </div>

    请注意,我在此 sn-p 中为下拉菜单添加了点击切换功能。

    【讨论】:

      猜你喜欢
      • 2011-06-13
      • 1970-01-01
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 2017-11-07
      • 2011-06-23
      • 2012-09-14
      相关资源
      最近更新 更多