I've been loving position: sticky
. It solves most, if not all, of the issues without resorting to JavaScript. But, I've hit a wall. I need to make an element that is nested inside a couple of <div>
to be sticky. We know that position: sticky
works as a blend of position: relative
and position: fixed
, therefore it will anchor to its first parent.
From MDN:
The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor)
In this case, I want to make a header sticky relative to the window and not the container. The HTML makes it difficult for me to restructure it outside nested <div>
Is this possible without JavaScript?
Here's the code:
<div class="attendance">
<!-- Here's the header I want to make sticky to the window, and not to div.attendance-->
<header class="text-center sticky">Monday 11/22/2019</header>
<!-- Header above -->
<div class="date-config">
<div class="form-group">
<input type="checkbox" id="workable" /> No Work<br />
</div>
<div class="form-group">
<label for="notes">Notes:</label>
<textarea id="notes" class="form-control"></textarea>
</div>
<label for="markall">Mark all as>
<select id="markall" class="form-control">
<option></option>
<option>Absent</option>
<option>Present</option>
</select>
</div>
<div class="student-attendance">
Hello :)
</div>
</div>
Any ideas?
P.S: I've found this, but it uses JavaScript.
Edit: Here's an awful, but working example (Beware! It's in Spanish - Look for the dates! They won't stick to the window!).
I've been loving position: sticky
. It solves most, if not all, of the issues without resorting to JavaScript. But, I've hit a wall. I need to make an element that is nested inside a couple of <div>
to be sticky. We know that position: sticky
works as a blend of position: relative
and position: fixed
, therefore it will anchor to its first parent.
From MDN:
The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor)
In this case, I want to make a header sticky relative to the window and not the container. The HTML makes it difficult for me to restructure it outside nested <div>
Is this possible without JavaScript?
Here's the code:
<div class="attendance">
<!-- Here's the header I want to make sticky to the window, and not to div.attendance-->
<header class="text-center sticky">Monday 11/22/2019</header>
<!-- Header above -->
<div class="date-config">
<div class="form-group">
<input type="checkbox" id="workable" /> No Work<br />
</div>
<div class="form-group">
<label for="notes">Notes:</label>
<textarea id="notes" class="form-control"></textarea>
</div>
<label for="markall">Mark all as>
<select id="markall" class="form-control">
<option></option>
<option>Absent</option>
<option>Present</option>
</select>
</div>
<div class="student-attendance">
Hello :)
</div>
</div>
Any ideas?
P.S: I've found this, but it uses JavaScript.
Edit: Here's an awful, but working example (Beware! It's in Spanish - Look for the dates! They won't stick to the window!).
Share Improve this question edited Nov 30, 2018 at 3:37 Jose A asked Nov 30, 2018 at 2:59 Jose AJose A 11.2k12 gold badges85 silver badges124 bronze badges 4- 2 can you also post your existing CSS so that we can run the code and see the current result? – yqlim Commented Nov 30, 2018 at 3:14
- @YongQuan: Sure! It's awful (Huge, and it's going to break your PC), but I added a jsfiddle with the exact code. – Jose A Commented Nov 30, 2018 at 3:38
-
position: sticky
is not supported in IE11. So be aware of that. Ideal approach would be to useposition: fixed
and let other element flow with defined margin-top. – Dinesh Pandiyan Commented Nov 30, 2018 at 3:38 - @DineshPandiyan: Thanks ;) This is more of an enhanced feature, so it's ok if it doesn't work with older browsers. – Jose A Commented Nov 30, 2018 at 3:40
2 Answers
Reset to default 3Ok! First I'd like to apologize as this question wasn't possible to be answered without rendering the HTML. Fortunately, I have found the solution.
TL;DR In this case, no, you need JavaScript. You will need to implement a translateY
transform in the element to achieve this. I don't know if the problem is that the parent element has a transform property and it causes this bug or there's something else causing the issue.
Explanation:
I'm currently using a carousel JS library called tiny slider. I'm displaying form elements instead of images, (Building a responsive table; Had issues when I tried using CSS Grids). So far, so good. The problem started when I wanted to set sticky
the date headers.
I went with the modern approach of setting position:sticky
, but that didn't work. The elements would get clogged in a certain position and it wouldn't move or stick. I started researching online (which ended up asking this same question), and the HTML itself. I did find that there were many parent <div>
s that were created by tiny-slider. My theory was that it was getting attached to one of those parents.
Therefore, I decided to try the old tactic of bining position:fixed
with a scroll
event. But, that didn't work. Going back online and Google-Fuing a bit, there seems to be an old bug [1] [2] [3] that whenever a translate is applied to one of the parents an out-of-root container is created and position:fixed
doesn't work as expected.
I have a hunch that this may be one of the reasons why sticky didn't work, but according to this answer, it doesn't seem like it.
I kept thinking for a while, and resorted to use a transform
CSS property with translateY
. I made a small experiment in the browser, and it worked!
Hence, I ended up implementing the scroll
eventListener and listening to the header's parent's position, and applying getBoundingClientRect() to get the offset. If I had applied it to the element itself, it would have given me the translated position which I applied through CSS.
I was skeptical that this could be a performance bottleneck for mobile browsers. Therefore, I checked that the transform function was called inside a requestAnimationFrame
and it had applied a will-change
property in the CSS stylesheet.
I ran the code with a 4x CPU Slowdown in Google Chrome, and had good results