Home > Blockchain >  How to have items in a flex container allow their nested children to appear below it onClick?
How to have items in a flex container allow their nested children to appear below it onClick?

Time:07-14

I have a flex container with flex direction row and a series of items in it that renders to the screen like the below image:

Image 1

After clicking on one of these items, I need a series of items to appear below it. For example, after clicking on 'Top Level Item 1' the below items should appear like in this image below:

Image 2

While I know it's possible to place two containers (one that contains all the top levels items and another that contains all the bottom level items) on top of each other and simply update the bottom one onClick, this causes alignment issues when more items are added or non-items are added to the top level.

Instead, I'm trying to find a way to have the bottom level items nested within its associated top level item. This approach works, but obviously it will push the border down like in the image below:

Image 3 How can I achieve the layout of Image 2 onClick, but still have the individual bottom level item divs nested within its associated top level item div?

For example, in a way similar to:

               <div>
                    <div>Top Level Item 1</div>
                    <div>
                        <div>bottom Level Item 1</div>
                        <div>bottom Level Item 2</div>
                    </div>
                </div>

Position: absolute on the dropdown items will not work if there is a height in place on the bordered nav and the items are centered within in. So I am trying to find a way to account for that use case as well. For example, this is what occurs if you place a height on the container and position: absolute on the dropdown items.

enter image description here

Thank you for any insight!

CodePudding user response:

Using position:absolute is indeed the solution (I started working on it and saw a comment that was a bit quicker).

I still wanted to do the challenge, let me know if this is the result you desire.

document.querySelectorAll('.level').forEach(el => {
  el.addEventListener('click', (ev) => {
    const toggleNode = [...el.childNodes].filter(child => child.classList && child.classList.contains('secondary'));
    
    if (toggleNode.length) {
        toggleNode[0].classList[toggleNode[0].classList.contains('hidden') ? 'remove' : 'add']('hidden');
    } 
  })
})
.flex {
  position: relative;
  display: flex;
  align-items: center;
  border: 1px solid black;
  height: 130px;
}

.level {
  flex-basis: 33%;
  flex-grow: 1;
}

.secondary {
  position: absolute;
  top: 130px;
}

.hidden {
  display: none;
}
<div >
  <div >
    <div>Top Level Item 1</div>
    <div >
      <div>bottom Level Item 1</div>
      <div>bottom Level Item 2</div>
    </div>
  </div>
  <div >
      <div>Top Level Item 2</div>
      <div >
        <div>bottom Level Item 1</div>
        <div>bottom Level Item 2</div>
    </div>
  </div>
  <div >
      <div>Top Level Item 3</div>
      <div >
        <div>bottom Level Item 1</div>
        <div>bottom Level Item 2</div>
    </div>
  </div>
</div>

CodePudding user response:

One option would be to put the border on the header elements.

<div>
  <div style="border: 1px solid black"> // Put the Border on this element
    Top Level Item 1
  </div> 
   
  <div>
    <div>bottom Level Item 1</div>
    <div>bottom Level Item 2</div>
  </div>
</div>

CodePudding user response:

Add a top and bottom border around the top level item, then using pseudo selectors add border-left to :first-of-type and add border-right to :last-of-type

You can set how many lines you want the title to use by adding:
  >   style: --topLevelLines: [line number];
to any parent .items element. Text that in the toggles that exceeds the line limit will be truncated. Text under the line limit will be vertically centered. Not setting a value will default to a single line.

The children below it is done without position: absolute so that they will push content below it down when expanding so it won't overlap anything.

const toggleActive = toggle => toggle.parentElement.classList.toggle('active');
* { box-sizing: border-box } body { font: 16px sans-serif; margin: 0 }


.items { display: flex }
.item { flex: 1 0 0% }

.toggle {
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  cursor: pointer;
  display: grid;
  height: calc( var(--topLevelLines, 1) * 1.25rem   ( .25rem * 2 ) );
  line-height: 1.25rem;
  overflow: hidden;
  padding: .25rem .5rem;
  place-items: center start;
}

.toggle > span {
  display: -webkit-box;
  -webkit-line-clamp: var(--topLevelLines, 1);
  -webkit-box-orient: vertical;
  -webkit-box-align: center;
  -webkit-box-pack: center;
}

.dropdown > div {
  max-height: 0;
  overflow: hidden;
  padding: 0 .5rem;
}

.active .toggle { background-color: lightgray }
.active .dropdown > div {
  margin-bottom: .125rem;
  max-height: none
}
.active .dropdown > div:first-of-type { margin-top: .125rem }
.active .dropdown > div:last-of-type { margin-bottom: .5rem }

.item:first-of-type .toggle { border-left: 1px solid currentColor }
.item:last-of-type .toggle { border-right: 1px solid currentColor }
<div >
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 1</span></div>
    <div >
      <div>bottom Level Item 1</div>
      <div>bottom Level Item 2</div>
    </div>
  </div>
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 2</span></div>
    <div >
      <div>bottom Level Item 3</div>
      <div>bottom Level Item 4</div>
      <div>bottom Level Item 5</div>
    </div>
  </div>
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 3</span></div>
    <div >
      <div>bottom Level Item 6</div>
      <div>bottom Level Item 7</div>
      <div>bottom Level Item 8</div>
      <div>bottom Level Item 9</div>
    </div>
  </div>
</div>

<div  style="--topLevelLines: 2">
  <div >
    <div  onclick="this.parentElement.classList.toggle('active')"><span>Top Level<br>Item 4</span></div>
    <div >
      <div>bottom Level Item 10</div>
      <div>bottom Level Item 11</div>
    </div>
  </div>
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 5</span></div>
    <div >
      <div>bottom Level Item 12</div>
      <div>bottom Level Item 13</div>
      <div>bottom Level Item 14</div>
    </div>
  </div>
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 6</span></div>
    <div >
      <div>bottom Level Item 15</div>
      <div>bottom Level Item 16</div>
      <div>bottom Level Item 17</div>
      <div>bottom Level Item 18</div>
    </div>
  </div>
</div>

<div  style="--topLevelLines: 3">
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 7 with lots of text in it that will eventually cause it to overflow the line restrictions Rhoncus arcu cum ac vestibulum volutpat a luctus parturient nascetur condimentum dui penatibus habitant vestibulum vestibulum euismod id parturient porta ullamcorper viverra ultricies per integer a. A non sit adipiscing dis orci eget ac mi mauris nunc vestibulum gravida nascetur a nisl proin sociis adipiscing netus sit. Per id est posuere a a varius habitasse imperdiet laoreet consectetur vestibulum vestibulum nec a sit euismod. Consectetur vel vitae interdum mollis dis integer etiam non adipiscing vestibulum tempor ligula ultricies laoreet semper libero ligula consequat mollis a scelerisque elit ac sed viverra. Orci per suspendisse a eleifend mus a primis a orci nam augue condimentum leo ullamcorper sem volutpat sit condimentum vestibulum a velit eros nibh non mi. Sodales lorem malesuada bibendum sem parturient ligula dis a vulputate orci suspendisse curabitur varius porttitor vestibulum adipiscing parturient nam nam cursus sagittis.</span></div>
    <div >
      <div>bottom Level Item 19</div>
      <div>bottom Level Item 20</div>
    </div>
  </div>
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 8</span></div>
    <div >
      <div>bottom Level Item 21</div>
      <div>bottom Level Item 22</div>
      <div>bottom Level Item 23</div>
    </div>
  </div>
  <div >
    <div  onclick="toggleActive(this)"><span>Top Level Item 9</span></div>
    <div >
      <div>bottom Level Item 24</div>
      <div>bottom Level Item 25</div>
      <div>bottom Level Item 26</div>
      <div>bottom Level Item 27</div>
    </div>
  </div>
</div>

  • Related