【问题标题】:Collapsible div in flex container opens the other divs as wellflex 容器中的可折叠 div 也会打开其他 div
【发布时间】:2019-12-28 12:48:43
【问题描述】:

我在 CSS flex 容器中有一组可扩展的 div。

Codepen - https://codepen.io/vijayvmenon/pen/bGNRwvd

CSS:

.collapsible {
  background-color: #777;
  color: white;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

.content {
  padding: 0 18px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
  background-color: #f1f1f1;
}

当我单击任何 div 时,该行中的其他 div 也会展开。如何通过仅扩展我单击的 div 而不扩展行中的其他 div 来解决此问题?

我正在使用 flex 容器,因为我正在构建一个响应式页面并且有更多组件,这里不包括。是否可以使用 flex/grid 容器?请告诉我。 我可以包装 div 并使其响应的任何其他解决方案都应该没问题。

我在使用聚合物框架时创建了一个类似的问题 - Collapsible div inside a CSS grid container expands full width

因为我在这里没有得到任何帮助,所以想用 Vanilla Javascript 创建一个。请帮忙。

注意:我事先不知道项目的实际数量,它是使用聚合物库中的“dom-repeat”呈现的。所以我只能有一个包含完整项目集的容器 div。我不能为每一列项目设置封闭的 div(除非有办法做到这一点)

【问题讨论】:

  • 其他 div 实际上并没有展开,因为您看不到隐藏的文本。
  • 这和布局有关吧?当一个 div 展开时,您可以检查元素并查看其他具有“内容”类的 div 的最大高度仍为 0。此外,如果您减小屏幕宽度并且 div 垂直堆叠,则问题不存在。我觉得它与 flex 容器有关。但不确定如何解决。
  • 这是因为当拥有flex-direction: row;flex-wrap: wrap; 时,您将创建包含每个项目的行。因此,如果它强制它,这些行会扩展为内部的项目。您需要一个列结构,其中项目可以垂直移动。但仅使用 CSS 更难解决。
  • 列数总是3吗?
  • 它是响应式的。列数取决于列表中的项目数。如果屏幕很大,例如在 Mac 中,由于 flex 容器和更多空间的可用性,它将显示三列。在较小的屏幕中,它将显示 2 列,而在移动设备中,它将垂直堆叠。

标签: javascript html css


【解决方案1】:

这是下面的代码(香草 JS,因为我不使用聚合物)。它是响应式的,并且会根据您的item 宽度调整列的数量(我假设您将使用media-query 进行更改)。这个想法是:

  • 通过创建一个虚拟项目来测量您在 CSS 中指定的 item 宽度
  • 根据测量的item 宽度调整所需的列数。有必要将项目包装在列中,因为通过将所有内容包装在一个单独的 flex 容器中来完成您想要的操作是不可能的。
  • 使用 vanilla JS 动态创建元素(在您的情况下使用聚合物)
  • 将项目逐列添加
  • 点击的逻辑还是和你的一样

这是完整的工作示例:

let container = document.querySelector('.container')
let columnAmount = (() => {
  let containerWidth = parseInt(getComputedStyle(container).width)
  let dummyItem = document.createElement('div')
  let itemWidth

  dummyItem.classList.add('item')	
  container.appendChild(dummyItem)
  itemWidth = parseInt(getComputedStyle(dummyItem).width)
  dummyItem.remove()

  return Math.floor(containerWidth / itemWidth)
})()

let newColumns = []
for (let i = 0; i < columnAmount; i++) {
  newColumns.push(document.createElement('div'))
  newColumns[i].classList.add('item')
  container.appendChild(newColumns[i])
}


let childAmount = 11 // Change this to your needs
let newChild
let newCollapsibleButton
let newContent
let newContentParagraph
for (let i = 0; i < childAmount; i++) {
  newChild = document.createElement('div')
  newCollapsibleButton = document.createElement('button')
  newContent = document.createElement('div')
  newContentParagraph = document.createElement('p')

  newChild.classList.add('item--inner')
  newCollapsibleButton.classList.add('collapsible')
  newContent.classList.add('content')

  newCollapsibleButton.appendChild(document.createTextNode(`Open Section ${i + 1}`))
  newContentParagraph.appendChild(document.createTextNode(`Section ${i + 1} Details`))

  newContent.appendChild(newContentParagraph)
  newChild.appendChild(newCollapsibleButton)
  newChild.appendChild(newContent)

  newColumns[i % columnAmount].appendChild(newChild)
}

let collapsibleButtons = document.querySelectorAll(".collapsible");
for (let i = 0; i < collapsibleButtons.length; i++) {
  collapsibleButtons[i].addEventListener('click', function() {
    let content = this.nextElementSibling;

    this.classList.toggle("active");
    content.style.maxHeight = content.style.maxHeight ? null : `${content.scrollHeight}px`
  });
}
.container {
  display:flex;
  justify-content:space-between;
  flex-wrap:wrap;
}

.item {
  width: 30%;
}

.item--inner {
  width: 100%;
}

.collapsible {
  background-color: #777;
  color: white;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

.content {
  padding: 0 18px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
  background-color: #f1f1f1;
}
<div class = "container">
</div>

请注意,它不处理页面大小调整,即项目将在页面加载时处于该固定状态(在必要时处理页面大小调整)。 为方便起见,调整此 JSFiddle 的屏幕尺寸:here 并查看它对不同屏幕尺寸的反应。在上面的 sn-p 上运行只允许加载一列项目,container 的大小非常有限。 我已经调整了 item 具有 item--inner 并允许 item 到始终为容器大小的 30%。如果您不希望它始终是其容器大小的 30%,只需更正 item 的 CSS 样式即可。为了更方便的代码调整,请参阅此处的 JSFiddle:here

注意:如果您想查看两列版本和垂直堆叠版本,请将 item 宽度改回 400px。确保在页面加载之前调整屏幕大小。

【讨论】:

  • 谢谢理查德。明天我将在 Polymer 中尝试一下,并检查它是否是可行的解决方案。会让你知道的。
  • 理查德,非常感谢这个解决方案。我能够在聚合物中实现这一点。非常感谢!
  • @VijayMenon 很高兴我能帮上忙。
【解决方案2】:

嗯,这是我的解决方案...

它在每一列上创建一个新的 div,并发送里面的所有项目。在窗口调整大小时,它会创建更多或更少的列,以尊重响应式案例

这是我的代码:

(function () {
  const Container = document.querySelector('.container')
    ,   All_item  = [...document.querySelectorAll('.item')]
    ;
  if ( All_item.length===0 ) return 

  const items_W = All_item[0].offsetWidth        // get item width 
    ,   divGpr  = document.createElement('div')  // base col element
    ,   flexCol = []                             // array of ihm cols
    ;
  divGpr.style.width = items_W+'px';             // perfect size

  function SetNewCols()   //
    {
    let nbCol = Math.max(1, Math.floor( Container.offsetWidth / items_W ) )
      , len   = flexCol.length;
    if (nbCol===len) return     // col unchanged

    for(let k=0;k<nbCol;k++)    // create new Col
      { flexCol.push( Container.appendChild(divGpr.cloneNode(true))) }

    let k=0;
    for(item of All_item)
      {
      flexCol[len+k].appendChild(item)  // dispach item in new col
      k = ++k % nbCol                   // future col of
      }
    for(i=0;i<len;i++)
      { Container.removeChild( flexCol.shift() ) } // remove old empty cols
    if(k>0)
      { flexCol[( flexCol.length -1)].appendChild(item) }
    }

  SetNewCols();  // first Attempt

  window.addEventListener('resize', SetNewCols); 

  document.querySelectorAll(".container button").forEach(Koll=>
    {
    Koll.onclick=e=>
      {
      let content = Koll.nextElementSibling;
      content.style.maxHeight = Koll.classList.toggle("active")
                              ? content.scrollHeight + "px"
                              : 0;
      }
    })
})();
.container {
  display:flex;
  justify-content:space-between;
  flex-wrap:wrap;
}
.container .item {
  width:400px;
}
.container .item > button {
  background-color: #777;
  color: white;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}
.container .item .content {
  padding: 0 18px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.2s ease-out;
  background-color: #f1f1f1;
}
  <div class="container">
    <div class="item">
      <button>Open Section 1</button>
      <div class="content">
        <p>Section 1 details</p>
      </div>
    </div>
    <div class="item">
      <button>Open Section 2</button>
      <div class="content">
        <p>Section 2 details</p>
      </div>
    </div>
    <div class="item">
      <button>Open Section 3</button>
      <div class="content">
        <p>Section 3 details</p>
      </div>
    </div>
    <div class="item">
      <button>Open Section 4</button>
      <div class="content">
        <p>Section 4 details</p>
      </div>
    </div>
    <div class="item">
      <button>Open Section 5</button>
      <div class="content">
        <p>Section 5 details</p>
      </div>
    </div>
    <div class="item">
      <button>Open Section 6</button>
      <div class="content">
        <p>Section 6 details</p>
      </div>
    </div>
  </div>

【讨论】:

  • @VijayMenon 我想,就您而言,至少有一个反馈......
  • 非常感谢您的解决方案。看起来真的很优雅,也可以调整大小。感谢您在这方面的努力。我赞成你的回答。我已经实施了理查兹解决方案并且工作正常。所以这仍然是我接受的答案。再次,非常感谢!
【解决方案3】:

你可以做的是创建一个类似列的结构,将所有列元素分组到一个带有类项的 div 中:

<div class="container">
  <div class="item">
    <div class="item">
      <button class="collapsible">Open Section 1</button>
        <div class="content">
          <p>Section 1 details</p>
        </div>
    </div>
    <div class="item">
      <button class="collapsible">Open Section 4</button>
      <div class="content">
        <p>Section 4 details</p>
      </div>
    </div>
  </div>
  ...
</div>

我把修改后的CodePen留给你!希望这是你想做的!

【讨论】:

  • 感谢何塞普的回复。让我在我的网页中尝试一下,看看它是否有效。我会在这里更新。
  • 我想我再次简化了这个问题。我实际上不知道里面的项目数量,它是使用聚合物库中的 dom-repeat 加载的。您的方法要求我事先知道元素的数量。为在实际问题中没有说清楚而道歉。我现在已经修改了。如果内容是动态的并使用重复功能加载,是否可以这样做?
【解决方案4】:

在我的方法中,我将使用$(document).on('click', '.collapsible', function () {});

let $collBtn = '.item > .collapsible'; // all collapse
    $(document).on('click', '.collapsible', function (e) {
        e.preventDefault();
        $($collBtn).removeClass('active');
        $(this).addClass('active');
    });

一旦.collabsible 有一个类active,它将像togleClass() 一样扩展元素。根据我有多个按钮并使用 $(document).on('click', '.item', function () {}) 的经验,这相当于在相同元素中找到一个类 && 按钮。希望这可以提供帮助。 :)

【讨论】:

  • 菲利克斯,我不明白你的解决方案。如果您可以使用 codepen 演示进行演示,将会很有帮助。
【解决方案5】:

@Vijay:- 这不是问题,而是正常行为。当您单击任何 div 时,该行中的其他 div 不会扩展,但内容会占据它的位置并将其他元素推到该行下方以提供显示内容的空间。您只能使用具有相同 HTML 结构的 Position 相对和绝对逻辑。

  *{
     box-sizing:border-box;
    }
    .item{
       position:relative;
    }
    .content {
      padding: 0 18px;
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.2s ease-out;
      background-color: #f1f1f1;
      position:absolute;
      top:100%;
      left:0;
      right:0;

    }

【讨论】:

    猜你喜欢
    • 2023-04-07
    • 2018-10-15
    • 1970-01-01
    • 2018-12-13
    • 2020-07-27
    • 1970-01-01
    • 2020-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多