12

Is there a way to prevent an absolutely positioned element from triggering a scrollbar when overflow-y is set? I was under the impression it would not, because absolutely positioned element normally do not affect the width/height of parent elements. For some reason this does not seem to matter for determining whether to show a scrollbar when overflow-y: auto is set. Is there a way to work around this without putting the dropdown contents in a completely different place in the DOM?

I would like only the things that would normally make an element grow be able to trigger a scrollbar.

overflow-y problem demonstration

Here is a codepen showing the issue:

http://codepen.io/isaksky/pen/zxedXe

Isak
  • 1,591
  • 1
  • 17
  • 23
  • Btw, here is the answer: http://www.w3schools.com/cssref/pr_pos_overflow.asp – Ismael Miguel Mar 24 '15 at 23:01
  • @JoshCrozier the quality of the content is great, but still... Also, here is a non-w3schools link: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow – Ismael Miguel Mar 24 '15 at 23:04
  • I have no idea why w3schools has received so much hate. It is usually far less complete than MDN's descriptions (but is usually fine for beginners). I find inaccuracies (which people are generally wont to complain about) very hard to come by on w3schools. Could somebody provide a link to a w3schools page that shows an inaccuracy? – Adam Jenkins Mar 24 '15 at 23:11

2 Answers2

4

IMO user agents render absolutely positioned elements within wrappers with overflow other than visible this way due to performance issues.

Because they have to redraw (and change the position of) the absolutely positioned elements with the respect to their containing block during scrolling.

The same thing happens in the presence of floats — See:


One possible option could be to let the absolutely positioned element position relative to the initial containing block (where the <html> element lives) instead.

In order to achieve that, we can remove position: relative from the #dialog-1, so the containing block of the absolutely positioned element would be the initial containing block. And also it gets scrolled along with the page.

Example Here

function forAllNodes(nodes, fn) {
  Array.prototype.forEach.call(
    nodes,
    function(node) {
      fn.call(node);
    }
  );
}

var layoutGate = function() {
  forAllNodes(
    document.querySelectorAll('.dropdown-contents'),
    function() {
      if (this.classList) this.classList.toggle('hide');
    }
  );
};

var intervalId = setInterval(layoutGate, 1000);

forAllNodes(
  document.querySelectorAll('.toggle-dropdown-btn'),
  function() {
    var elm = this;
    elm.addEventListener('click', function() {
      if (intervalId) {
     clearInterval(intervalId);
     intervalId = null;    
      }
      layoutGate();
    }, false);
  }
);
.wrapper {
  padding-left: 20px;
}

p {
  line-height: 1.3em;
}

#dialog-1 {
  padding: 10px;
  border: 3px solid black;
  width: 400px;
  min-height: 100px;
  max-height: 150px;
  overflow-y: auto;
}

.dropdown-control {
  /* position: relative; */
  margin: 0;
  padding: 0;
}

.toggle-dropdown-btn {
  padding: 0;
  margin: 0;
}

.dropdown-contents {
  list-style-type: none;
  position: absolute;
  /* min-width: 100%; */
  width: 400px;
  background-color: #81d4fa;
  margin: 0;
  padding: 0;
  border: 1px solid green;
  /* top: 100%; */
  /* left: 0; */
}

#dialog-2 {
 padding: 10px;
  border: 3px solid black;
  width: 400px;
  min-height: 100px;
  max-height: 150px;
}

.hide {
  display: none;
}
<div class="wrapper">
  <h1>Div with overflow-y:</h1>
  <div id="dialog-1">
    <p>Stuff in Dialog blah blah</p>

    <div class="dropdown-control">
      <button type="button" class="toggle-dropdown-btn">Toggle Dropdown</button>
      <ul class="dropdown-contents">
        <li>why</li>
        <li>do i </li>
        <li>trigger </li>
        <li>scroll</li>
      </ul>  
    </div>
  </div>

  <h1>Normal div, no overflow-y</h1>
  <div id="dialog-2">
    <p>Stuff in Dialog blah blah</p>
    <div class="dropdown-control">
      <button type="button" class="toggle-dropdown-btn">Toggle Dropdown</button>
      <ul class="dropdown-contents">
        <li>this</li>
        <li>does not </li>
        <li>grow </li>
        <li>the div</li>
      </ul>  
    </div>
  </div>
</div>
Community
  • 1
  • 1
Hashem Qolami
  • 97,268
  • 26
  • 150
  • 164
  • 1
    Good Info and links, thanks. I could see the it being hard to make it performant if there is a scrollbar. But if there currently is no scrollbar, and an absolutely positioned element becomes visible, is there a perf reason to add one then? – Isak Mar 25 '15 at 22:38
  • @Isak Good question. Actually that was a pure speculation; Perhaps we should ask it from web browser manufacturers or from CSS working group by their [mailing list](http://lists.w3.org/Archives/Public/www-style/) but my understanding is that while there may be no scrollbars at first, the potential exists for the size of the box to be increased either by wrapping the contents in lower screens or by adding additional content dynamically by JavaScript. Hence, no matter that scrollbar is appeared at first or not, the absolutely positioned element will be affected by its parent's `overflow`. – Hashem Qolami Mar 25 '15 at 23:41
  • 1
    @Isak Also you might find [these](https://css-tricks.com/popping-hidden-overflow/) [topics](http://stackoverflow.com/questions/2243245/css-how-to-have-positionabsolute-div-inside-a-positionrelative-div-not-be-cro) [useful](http://stackoverflow.com/questions/5513382/absolute-position-and-overflowhidden). – Hashem Qolami Mar 25 '15 at 23:44
  • @eitanfar Thanks for adding the JS part to the snippet; I changed that to pure JavaScript version BTW :-) – Hashem Qolami Feb 11 '16 at 07:06
  • @HashemQolami Sure ! Another tiny bit that you missed originally was the definition of the class "hide" which was the one controlling the "display:none". By the way, thanks for your answer, it helped me with a much more complicated problem as well :-) – ethanfar Feb 11 '16 at 10:17
0

Very simple answer - no, there is no workaround because you are not fixing a bug, or inaccurate implementation. If you want to use HTML/CSS, then you have to play by it's rules. What you can do is make your absolutely positioned div a sibling of your overflow-y: auto div, but, as you've seen, it cannot be a child of it.

.container { position: relative; }
.scroll-container { overflow-y: auto; border: 3px solid #000; height: 90px; }
.abs { position: absolute; top: 100%; margin-top: -10px; left:0; right:0; background:#0f0; padding: 50px;}
<div class="container">
  
  <div class="scroll-container">
    this <br>
    is <br>
    an <br>
    overflow <br>
    div <br>
    that <br>
    scrolls <br>
  </div><!-- .scroll-container -->
  
  <div class="abs">
    an absolutely positioned div
  </div><!-- .abs -->
  
</div><!-- .container -->
Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100