How to prevent an absolutely positioned element to affect the scrollbar?

  • When autosuggest suggestions are shown, they should appear on top of the content without affecting container's scrollbar.
  • When scrolling the container, the input and suggestions should move together.

This can't be done with CSS only.

To have suggestions appear on top of the container's content, non clipped, it has to have position: absolute and none of its parents (autosuggest and container) can be position: relative.

The down side is that suggestions will not move on scroll.

For suggestions to move with scroll, one of its parents (autosuggest or container) needs to be position: relative.

The down side with that is, and as the container's is not overflow: visible, it will be clipped


As already suggested, and assumed the input has to be within the autosuggest element, changing the position: relative on the autosuggest to position: absolute, so the input stays with suggestions on scroll, will likely be the best, though setting z-index on each container will be needed to avoid odd overlapping.

But if the provider of the third party component,... :) ..., could be talked into a version where the input could be placed outside the autosuggest element, one could get some more control, using CSS only, of both the suggestions and the content and their layouts, based on if input has focus or not,...

... where this sample maybe could be a good start (click on input to show suggestions).

.container {
  background-color: white;
  width: 300px;
  min-height: 100px;
  margin-top: 50px;
  border: 1px solid black;
  overflow: auto;
  position: relative;
}
.autosuggest {
  position: relative;
  width: 250px;
  z-index: 1;
}
.input {
  font-size: 16px;
  width: 245px;
  padding: 5px 10px;
  border: 0;
  background-color: #FFEBBF;
  position: relative;
  z-index: 1;
}
.suggestions {
  list-style-type: none;
  margin: 0;
  padding: 5px 10px;
  width: 245px;
  background-color: #85DDFF;
  display: none;
}
.content {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  padding: 35px 10px 5px 10px;
  overflow: auto;
  z-index: 0;
}
input:focus ~ .autosuggest .suggestions {
  display: block;
}
<div class="container">
  <input class="input" type="text" value="input">
  <div class="autosuggest">
    <ul class="suggestions">
      <li>suggestion 1</li>
      <li>suggestion 2</li>
      <li>suggestion 3</li>
      <li>suggestion 4</li>
      <li>suggestion 5</li>
      <li>suggestion 6</li>
      <li>suggestion 7</li>
      <li>suggestion 8</li>
      <li>suggestion 9</li>
    </ul>
  </div>
  <div class="content">
    content content content content content content content content content content
    content content content content content content content content content content
    content content content content content content content content content content
    content content content content content content content content content content
    content content content content content content content content content content
    content content content content content content content content content content
  </div>
</div>

<div class="container">
  <input class="input" type="text" value="input">
  <div class="autosuggest">
    <ul class="suggestions">
      <li>suggestion 1</li>
      <li>suggestion 2</li>
      <li>suggestion 3</li>
      <li>suggestion 4</li>
      <li>suggestion 5</li>
    </ul>
  </div>
  <div class="content">
    content content content content content content content content content content
    content content content content content content content content content content
    content content content content content content content content content content
  </div>
</div>

This seems a bit of a janky approach and has a small caveat, but it's pure CSS/ no HTML structure modifications.

Essentially, I make the .container the main parent instead of trying to work from a lower level (.autosuggest). Step by step:

  • Move position: relative up to .container
  • Make the .autosuggest positioned absolutely (top / left default to 0px).
    • Give it a higher z-index so it's always on top
  • make .content positioned absolutely all four sides 0px so it's same size as .container
  • Move the overflow scrollbar to the .content div
  • (here's the caveat) Set the top padding of .content to the height of .input + the actually desired padding. Otherwise the .content is behind the input element.

And you end up with this:

    .container {
      height: 100px;
      width: 300px;
      margin-top: 50px;
      border: 1px solid black;
      position: relative;
    }
    .autosuggest {
      width: 250px;
      position: absolute;
      z-index: 50;
    }
    .input {
      font-size: 16px;
      width: 230px;
      padding: 5px 10px;
      border: 0;
      background-color: #FFEBBF;
    }
    .autosuggest .input:focus ~ .suggestions{
      display: block;
    }
    .suggestions {
      display: none;
      list-style-type: none;
      margin: 0;
      padding: 5px 10px;
      width: 230px;
      background-color: #85DDFF;
    }
    .content {
      overflow-y: auto;
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      padding: 28px 10px 5px;
    }
<div class="container">
  <div class="autosuggest">
    <input class="input" type="text" value="input">
    <ul class="suggestions">
      <li>suggestion 1</li>
      <li>suggestion 2</li>
      <li>suggestion 3</li>
      <li>suggestion 4</li>
      <li>suggestion 5</li>
      <li>suggestion 6</li>
      <li>suggestion 7</li>
      <li>suggestion 8</li>
      <li>suggestion 9</li>
    </ul>
  </div>
  <div class="content">
    content content<br >content content content content content<br ><br ><br ><br ><br ><br >content content content
  </div>
</div>

I also added some showing/ hiding for the autosuggest box, because it looks nice.

.autosuggest .input:focus ~ .suggestions{
  display: block;
}
.suggestions {
  display: none;
}

Working jsFiddle.

Tested FF 43, Chrome 47


When scrolling the container, the input and suggestions should move together.

This will most likely require JavaScript. By definition, setting the suggestions to absolute position removes them from the rest of the flow of content. You could set a height on the .suggestions div and scroll it separately, but that scrolling can't (to my knowledge) be tied to the .content scroll if it's positioned absolutely (again, using CSS alone).

Something like:

$('.content').on('scroll', function () {
  $('.autosuggest .suggestions').scrollTop($(this).scrollTop());
});

Why absolutely positioned suggestions affect container's scrollbar?

Technically speaking, absolutely positioned elements do not affect the parent's height. However, they're still viewed as content of the element - content that overflows (at least in this case).

The overflow property specifies whether to clip content, render scrollbars or just display content when it overflows its block level container. (from MDN)

Since .container has overflow set to scroll, it shows a scroll bar because one of it's children overflows the bounding box. If you change overflow to hidden it'll hide all extending content and set to visible you'll see the .autosuggest extend past the .container, but so does .content (since it's also content that extends the bounding box).

You see a scrollbar because the .suggest content visually extends beyond the .container block, even though it's positioned absolutely.