0

There are at least 2000 questions about this but in my particular scenario nothing I've tried worked.

  • My page consists of 2 nested divs (external, contents)
  • contents is clickable and should cover 100% of the document
  • there's also a bunch of absolutely positioned divs inside contents: those are draggable by a user and can be in any position.

If there are enough of these divs, scrollbar appears. And my contents is limited by the current browser window height, so when I start to scroll it's cut:

enter image description here

Here's a codepen: https://codepen.io/sergey-kritskiy/pen/qBbqQJv

I've tried...

  • setting min-height of everything one by one and all together;
  • adding flex on everything; float like this, float like that;
  • use %, vh, vmax;

When the scrollbar is made by a bunch of 'normal' divs, min-height works fine, but with these absolute guys I'm not in luck. I probably miss something obvious and I'd appreciate solutions.

Update:

There was an answer from someone that suggested to add position: relative; overflow-y: auto to contents and it worked in my case! But the answer was removed before I was able to ask why exactly that worked.

Sergey Kritskiy
  • 2,199
  • 2
  • 15
  • 20
  • Absolute positioned elements are removed from flow and thereby ignored by other elements. So you can't set the parents height according to an absolutely positioned element. You either use fixed heights or you need to involve some js code. – kristofvdj88 Jun 18 '20 at 06:16
  • based on the duplicate, the containing block of position:absolute is the nearest positionned ancestor so you need to add position:relative to their parent and then you can the overflow – Temani Afif Jun 18 '20 at 08:20

1 Answers1

1

Solution 1

  • wrap items in another div called sidebar.
  • i am appending items in .sidebar instead of #content.

  • you can use grid in #external like i have used in css

var target = document.querySelector(".sidebar");

for (var i = 1; i <= 30; i++)
{
  (function(i)
  {
    var div = document.createElement('div');
    target.appendChild(div);
    div.innerHTML = 'el ' + i;
    div.className = 'item';
    div.style.left = '5px';
    div.style.top = 5 + ((i-1) * 105) + 'px';
    div.addEventListener('click', function (evt) {
      console.log("clicked el " + i);
      evt.stopPropagation()
    });
  })(i);
}

target.addEventListener('click', function () {
  console.log("content click");
});
body,
  html {
    width: 100%;
    height: 100%;
    margin-right: auto;
    margin-left: auto;
    vertical-align: middle;
    margin: 0px;
    background-color: white;
  }

  .item {
    position: absolute;
    background-color: blue;
    width: 200px;
    height: 100px;
    border: 1px solid black;
  }

  #external {
    background-color: #5b6f59;
    display: grid;
    grid-template-columns: 230px 1fr;
  }

  #content {
    background-color: #5b6f59;
    position: relative;
  }

  #external,
  #content {
    width: 100%;
    height: 100%;
  }
  
/** Added CSS **/

.sidebar {
  height: 100vh;
  overflow-y: auto;
  position: relative;
}
<div id="external">
    <div class="sidebar"></div>
    <div id="content"> </div>
  </div>

Solution #2

var target = document.querySelector("#content");

for (var i = 1; i <= 30; i++)
{
  (function(i)
  {
    var div = document.createElement('div');
    target.appendChild(div);
    div.innerHTML = 'el ' + i;
    div.className = 'item';
    div.style.left = (Math.random()*100) + 'px';
    div.style.top = 5 + ((i-1) * 105 + Math.random()*100 - 50) + 'px';
    div.addEventListener('click', function (evt) {
      console.log("clicked el " + i);
      evt.stopPropagation()
    });
  })(i);
}

target.addEventListener('click', function () {
  console.log("content click");
});
body,
  html {
    width: 100%;
    height: 100%;
    margin-right: auto;
    margin-left: auto;
    vertical-align: middle;
    margin: 0px;
    background-color: white;
  }

  .item {
    position: absolute;
    background-color: blue;
    width: 200px;
    height: 100px;
    border: 1px solid black;
  }

  #external {
    background-color: red;
  }

  #content {
    background-color: #5b6f59;
    height: 100vh;
    overflow-y: auto;
    position: relative;
  }

  #external {
    width: 100%;
    height: 100%;
  }
  <div id="external">
    <div id="content">
    </div>
  </div>
Zuber
  • 3,393
  • 1
  • 19
  • 34
  • @Sergey Kritskiy let me know if you need more clarification. – Zuber Jun 18 '20 at 06:33
  • thank you for your answer, I think in order to make my example simple I oversimplified it: sorry about that. I actually _need_ those items to be absolute and they aren't necessary are in order. I updated my question and codepen – Sergey Kritskiy Jun 18 '20 at 07:42
  • There was an answer from someone that suggested to add `position: relative; scroll-y: auto` to `contents` and it worked in my case! But the answer was removed before I was able to ask why exactly that worked – Sergey Kritskiy Jun 18 '20 at 07:46
  • @SergeyKritskiy i have updated my answer. `item` are now `absolute` – Zuber Jun 18 '20 at 08:08
  • @SergeyKritskiy i have added another solution. – Zuber Jun 18 '20 at 08:19
  • Thank you Zuber, second solution has solved it for me. – Sergey Kritskiy Jun 18 '20 at 08:44