Is it possible to create a sticky element that stays fixed to the window without using JavaScript?

Using position: sticky has been a game-changer for me. It resolves many issues without the need for JavaScript. However, I've encountered a roadblock. I am trying to create a sticky element that is nested inside multiple <div> elements. Since position: sticky functions as a blend of position: relative and position: fixed, it naturally anchors to its first parent.

As per MDN's explanation:

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 my case, I'm looking to make a header sticky in relation to the window rather than its container. Unfortunately, the structure of the HTML complicates moving it outside the nested <div>.

Is there a way to achieve this without using JavaScript?

Below is the code snippet:

<div class="attendance">
<!-- The header I want to stick 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>

Does anyone have suggestions on how to tackle this issue?

P.S: While researching, I came across this resource, but it relies on JavaScript.

Edit: Here's an unconventional yet functional demo (Note: It's in Spanish - Focus on the dates! They do not stay anchored to the window!).

Answer №1

Apologies for the inconvenience, but answering this question required a closer look at the rendered HTML. Fortunately, I have identified a solution.

TL;DR In this scenario, JavaScript is necessary to achieve the desired outcome. You will need to apply a translateY transform to the element. It seems that the presence of a transform property in the parent element may be causing an issue as mentioned in this post.

Explanation:

I am currently utilizing a carousel JS library known as tiny slider. Instead of images, I am displaying form elements to create a responsive table (as CSS Grids were problematic). Initially, everything seemed fine until I attempted to make the date headers sticky.

Despite using position:sticky, the elements failed to stick properly and remained fixed in one position. After some online research and examining the HTML structure, I discovered multiple parent <div> elements created by tiny-slider. My suspicion was that the sticky behavior was being affected by one of these parents.

As a workaround, I tried combining position:fixed with a scroll event, but unfortunately, it did not resolve the issue. Further investigation led me to potential bugs associated with transforms applied to parent elements interfering with position:fixed functionality[1] [2] [3].

While there was speculation that this bug might explain the issue, an answer referenced here suggested otherwise.

After much contemplation, I decided to utilize the transform CSS property with translateY. This approach proved effective when tested in the browser.

Subsequently, I implemented a scroll eventListener to monitor the header's parent position and adjust accordingly using getBoundingClientRect(). To optimize performance on mobile browsers, I ensured that the transform function was called within a requestAnimationFrame and included a will-change property in the CSS stylesheet.

Testing the code under a 4x CPU Slowdown in Google Chrome yielded positive results 😁.

Below is the function I developed to handle fixed header scrolling (where elemsToFixed are all the <header> elements and threshold is the top offset):

export function fixedHeaderScroll(elemsToFixed: HTMLHeadingElement[], threshold: number) {
  if (!elemsToFixed || elemsToFixed.length === 0) {
    console.error("elemsToFixed can't be null or empty");
    return;
  }
  console.log('Total elems', elemsToFixed.length);
  const firstEl = elemsToFixed[0];
  let propSet = false;
  window.addEventListener('scroll', (e) => {
    window.requestAnimationFrame(() => {
      const top = firstEl.parentElement!.getBoundingClientRect().top;
      if (top > threshold) {
        if (!propSet) return;
        propSet = false;
        setElemsFixed(elemsToFixed, top, threshold, false);
        return;
      }
      propSet = true;
      setElemsFixed(elemsToFixed, top, threshold);
    });
  });
}

function setElemsFixed(elemsToFixed: HTMLHeadingElement[], top: number,
                       threshold: number, setFixed = true) {
  console.log('SetElemsFixed is', setFixed);
  elemsToFixed.forEach((elem) => {
    if (!setFixed) {
      elem.removeAttribute('style');
      return;
    }

    elem.style.transform = `translateY(${(top * -1)}px)`;
  });
}

The following image illustrates a 4x slowdown in CPU performance where the calculation of styles (with 26 elements) took approximately 29.4ms – a favorable outcome! Tested on Chrome 70 with Windows and an i7 4700MQ processor.

https://i.stack.imgur.com/AeAUb.png

Answer №2

When looking at the documentation, it mentions that the position sticky element will adhere to its closest scrollable ancestor. This usually means any ancestor with 'display: block' (which is the default) or even elements with 'display: flex'. To make an ancestor unscrollable, you can achieve this by setting 'display: contents'. However, keep in mind that its usability may vary depending on the rest of your layout and CSS.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm having trouble getting Grunt Source Maps to function properly within the foundation-press theme

I'm struggling to enable source maps for the npm package grunt-sass. Here's a snippet from my Gruntfile.js: The issue lies in this line: sourceMap: true, at line 13 module.exports = function(grunt) { var jsApp = [ 'js/app.js' ...

The subsequent code still running even with the implementation of async/await

I'm currently facing an issue with a function that needs to resolve a promise before moving on to the next lines of code. Here is what I expect: START promise resolved line1 line2 line3 etc ... However, the problem I'm encountering is that all t ...

Error encountered in a Node.js Express application: 'Error in Jade template (version 1.0+): The usage of duplicate key "id" is not permitted.'

Seeking guidance on the following issue: Within my Express app, I am providing numerous parameters to a Jade template, resulting in an error message that states: Duplicate key "id" is not allowed. (After reviewing, I have confirmed that there is no para ...

What is the best way to apply maximum height to an element using CSS?

Looking for help with my website: I made some changes in the code using Firebug and identified these elements that I want to modify (but changes are not live yet) The following code represents the elements on my site: This is the HTML code: <di ...

Triggering the body onunload event

I am currently developing a HTA that needs to make final modifications on the onunload event. However, I am facing an issue as the event does not appear to be triggered. Can someone confirm if this event is still supported? Is there an equivalent event in ...

How can I define margins for a specific div in print media using CSS?

Hello everyone, I'm currently experiencing an issue with my print media CSS. On the HTML page, there are two main divs - one for the body and one for the footer. <div class="main-template-body" id="main-template-body"> //content </div> ...

Shared validation between two input fields in Angular 2+

I have a unique task at hand. I am working on creating an input field with shared validation. The goal is to ensure that both fields are technically required, but if a user fills in their email address, then both fields become valid. Similarly, if they ent ...

What is the best way to center images horizontally in CSS and HTML?

I'm looking to create a driver's page where the desktop view displays images horizontally instead of vertically. I've tried adjusting the display attribute with limited success, and so far, the grid display is the closest I've come to a ...

What is the best way to adjust the size of the close button in react-bootstrap for a unique design?

<CancelButton className="exitBtn"/> .exitBtn{ height:40px; width:35px; } I'm trying to adjust the dimensions of the cancel button, but for some reason it's not working. Can someone provide guidance on how to successfully ...

Step-by-step guide on incorporating a new JSON object into an array to display its elements as components on a webpage

Could I adjust the state of an array by incorporating values from a data.js file as a starting point? The process involves executing the setAllThingsArray function to add a new element, determined by the setThingsArray function based on the previous state ...

conceal a component within a different container

Is there a way to hide or show an element within a container based on the hover state of another container? Here is an example of what I have tried so far: HTML5 <div class="left-menu-container"> <div class="left-menu-inner-container"> ...

How does the use of <ol> with list-style-type: disc; differ from that of <ul>?

Why create a separate <ul> when visually both unordered lists and ordered lists look the same? <head> <title>Changing Numbering Type in an HTML Unordered List Using CSS</title> <style> ol { list-s ...

Implementing a restricted Mongoose promise loop iteration count

Currently, I am developing an online store using Node, Express, and Mongoose. In the postCheckout Controller, which is responsible for handling user purchases, I am facing an issue. When a user buys a product with a quantity of 5, the code should check i ...

Is there a way to display various data with an onClick event without overwriting the existing render?

In the process of developing a text-based RPG using React/Context API/UseReducer, I wanted to hone my skills with useState in order to showcase objects from an onclick event. So far, I've succeeded in displaying an object from an array based on button ...

What could be causing site container not to respond to height:auto?

I have encountered an issue while developing a website using absolute height values. I am puzzled as to why the height:auto property is not working for me. Can anyone provide some insight on this? HTML Structure <div id="site-content"> <div id=" ...

How can I transfer a particular data value from a div to JavaScript within Laravel 5.0?

Displaying separate square divs based on integers retrieved from the database. This is the front-end view. I want to pass the room ID (code) to a JavaScript function when clicking on these div elements. https://i.stack.imgur.com/aIYTr.png Below is my cur ...

determining CSS selector

Having trouble identifying a locator using CSS. There are 10 elements with the same locator name on the webpage. Each web element has the same XPath value: //div[@class='thumb_image'] The web element list size is 10. If I want to access the 5t ...

The request header fails to function properly when used for cross-domain Ajax requests

I'm facing a challenge with adding a parameter in the request header. It works smoothly for calls within the same domain, but when making a call to a different domain (the API), I need to adjust the header parameter itself. Here is the snippet of cod ...

Enhancing image clips using jQuery techniques

Could someone help me figure out why the image clip value isn't changing when I move the range slider in the code below? $('#myRange').on('input', function() { var nn = $(this).val(); $("img").css({ 'clip': ...

Problems with radio button serialization in jQuery form plugin

I've created a basic form: <form class="dataform" method="post" id="settings" action="/"> <input type="radio" name="shareSetting" value="n"/> <input type="radio" name="shareSetting" value="y"/> <input type="button" na ...