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.