What causes the scroll position to change in Chrome when adjusting the height of a 'position:sticky' element, while Safari remains unaffected?

There is an interesting distinction in the way Chrome and Safari handle the CSS property position: sticky on elements.

When a sticky element increases in height, while being offset from its initial position due to scrolling, Chrome adjusts the scrollTop position accordingly, making it seem like the content remains stationary as the sticky element expands over it.

https://i.sstatic.net/7rLn2.gif

In contrast, Safari keeps the scrollTop position constant in this scenario, resulting in the appearance of the content shifting down to accommodate the taller sticky element.

https://i.sstatic.net/csSd2.gif

Below is a code snippet demonstrating how each browser behaves in this situation. The accompanying screenshots illustrate the behavior of this demo on Chrome and Safari, but you can try it out yourself here.

function grow() {
  const header = document.getElementById("header");
  document.getElementById("header").classList.toggle("large-header");
  updateScrollText();
}

function updateScrollText() {
const container = document.getElementById("container");
const scrollParent = getScrollParent(container);
  document.getElementById("scrollbarpos1").innerHTML = scrollParent.scrollTop;
  document.getElementById("scrollheight1").innerHTML = scrollParent.scrollHeight;
  document.getElementById("containerheight1").innerHTML = container.offsetHeight;
  document.getElementById("scrollbarpos2").innerHTML = scrollParent.scrollTop;
  document.getElementById("scrollheight2").innerHTML = scrollParent.scrollHeight;
  document.getElementById("containerheight2").innerHTML = container.offsetHeight;
}

function getScrollParent(node) {
  if (node == null) {
    return null;
  }

  if (node.scrollHeight > node.clientHeight) {
    return node;
  } else {
    return getScrollParent(node.parentNode);
  }
}


window.onscroll = updateScrollText; 
window.onload = updateScrollText;
#header {
  background-color: #CACACA;
  position: sticky;
  top: 0;
  padding: 20px;
}

.large-header {
  height: 100px;
}

.content {
  background-color: #a2a6c4;
  height: 1500px;
}

.shift-down {
  margin-top: 50px;
}
<div id="container">
  <div id="header">
    <button type="button" onclick="grow()">Grow/Shrink</button>
  </div>
  <div class="content">
    <br>
    Scrollbar position: <span id="scrollbarpos1">0</span>
    <br>
    Scroll height: <span id="scrollheight1">0</span>
    <br>
    Container height: <span id="containerheight1">0</span>
    <br>
    <br>
    Voluptatibus omnis perspiciatis consequatur magni error exercitationem saepe qui. Ipsa sint non labore voluptates. Asperiores aut non ullam aut sit omnis ducimus in. Aut enim nihil unde ad expedita. Ratione necessitatibus quasi dolorem sunt aperiam nobis ducimus.
Sequi quasi maiores eos aut non. Ipsam delectus sit facilis aut. Dolor facilis eum dignissimos. Vero reiciendis odio quis blanditiis.
Error nesciunt rem facilis. Neque labore et qui sequi eos corrupti dolorem. Reprehenderit qui voluptatem et neque ducimus ipsum similique fugit. Ea sint alias qui laborum nesciunt. Nihil ex repellendus odit sint unde fuga.
A eum nulla ut cumque necessitatibus culpa exercitationem unde. Corrupti sit minima eveniet et aut possimus sapiente. Est accusantium aut ut numquam illo.
Praesentium fugit pariatur eum ad velit distinctio culpa id. Quia voluptatum dignissimos consequatur. Eaque nihil voluptas in voluptas voluptas eius voluptas.
    <br>
    <br>
    
    <div class="shift-down">
    Scrollbar position: <span id="scrollbarpos2">0</span>
    <br>
    Scroll height: <span id="scrollheight2">0</span>
    <br>
    Container height: <span id="containerheight2">0</span>
    </div>
  </div> 
</div>

I delved into the W3C spec on Positioned Layout, but did not find a definitive explanation for this discrepancy.

Here are my questions:

  • Why do these two browsers exhibit different behaviors?
  • Is there a "correct" way that one of them follows?
  • Can both browsers be made to behave consistently in either direction?

Answer №1

What is the reason for this discrepancy in behavior between these two browsers?

To comprehend this, it is essential to grasp how a sticky element occupies space on a webpage. As you scroll beyond the sticky element, the displayed portion of the sticky element trails the top of your viewport, but its physical presence remains where it was originally positioned:

https://i.sstatic.net/MzyiY.png

This presents a dilemma for the browser when you resize the header because it pertains to an element that has already been scrolled past. There are two methods to resolve this issue. Safari and Firefox opt to always maintain the same distance from the top of the document, whereas Chromium adjusts the viewport to track the currently viewed element:

https://i.sstatic.net/bV65m.png

I believe Safari and Firefox adhere to their approach due to its simplicity, which aligns with longstanding browser conventions. Chromium recently altered its method as it offers a distinct benefit for users reading articles with sluggish-loading ads that alter size post-loading:

https://i.sstatic.net/jIUdO.png

Upon ad loading in Safari and Firefox, significant page content displacement occurs, resulting in user annoyance and disorientation. This can be particularly troublesome if multiple ads load consecutively. With the Chromium approach, the viewport adjustment ensures seamless browsing without noticeable disruptions.

I couldn't find specific specifications or discussions to substantiate my claims; however, I strongly believe this rationale is behind the observed behavior. It remains unclear whether Firefox and Safari deliberately chose not to adopt Chromium's approach, or if they deemed it inconsequential.

Which approach, if any, is considered "correct"?

Determining the superior solution likely entails a complex and somewhat subjective debate that I won't delve into, though Chromium's approach undeniably offers benefits.

Is there a way to achieve uniform behavior across both browsers (regardless of the chosen method)?

When activating the Grow/Shrink button, detecting any alterations in scroll height allows for prompt restoration to original settings. Conversely, manually adjusting the scroll height can standardize the behavior across all browsers akin to Chromium's functionality. An earlier article details this exact process:

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

Unlocking the power of AngularJS scopes

I am currently in the learning stages of AngularJS and am working on a personal project that involves fetching JSON data and displaying it on an HTML page using AngularJS. The main objective is to have two columns, "rules" and "servername", with checkbox i ...

jQuery providing incorrect measurements for an element's height

I am encountering an issue with obtaining the height of the #header element in my AngularJS application. Despite using jQuery, the returned height seems to be incorrect. The current code snippet looks like this: $(document).ready(function() { function ...

The accordion is experiencing functionality issues

I created a customized Accordion based on a code snippet I found online to incorporate into my project, but unfortunately, it's not functioning as expected. Currently, it is displaying the address of the high school and failing to hide it when the pag ...

Ways to refresh a component on a webpage without having to refresh the entire page

I am looking to dynamically load a specific component on my webpage when another component is altered <input ..... onchange="callFunctionToReloadNextTag()"> <input ... /> <--This is the tag I want to reload Is it possible to reload th ...

Having trouble getting a regular select list to work with angular-ui-select

Can anyone help me convert my normal select list code to the angular-ui-select directive code? This is my current HTML code: <select class="input-small tight-form-input" ng-model="panel.valueName" ng-options="f.value as f.text for f in bigValueOptions ...

Extracting dynamically generated styles from CSS tags

I'm currently trying to extract information from the specific element shown here: snapshot of HTML layout This element has a structure like div class="css-exfvnn excbu0ji", which was relatively easy to access at first, but the middle part appears to ...

Is there a way to decrease the size of the content within this iFrame?

Is there a way to display 6 cameras on a single webpage without them taking up the entire screen within iframe windows? Can the content be shrunken to fit the iframe window? <!doctype html> <html> <head> <meta charset="utf-8"> &l ...

Place the delete and edit buttons on the right side of the card

I am trying to align the edit and delete buttons with the image and premise name on the same line. Both buttons should be placed on the right side and have the same size as the card. Currently, the layout looks like this: https://i.sstatic.net/sNikU.png ...

What is the process for determining the area of the section?

Take a look at this page as an example - scrolling down on responsive devices reveals related navigation areas which I aim to change with scrollspy (affix fixed navigation). Having a bootstrap navbar, when on the corresponding section of the navbar while u ...

The periodLookup array does not have a defined value for periodStr. Why is this error being caught?

Here is a method that I am working with: set_period_help_text: function(periodInput){ var metric = MonitorMetric.getSelectedMetric(); var periodStr = $('select[name=metric_period]').val(); var datapoints = Number(periodIn ...

Can you provide guidance on showcasing mongodb data in a flask template with flask_pymongo?

Apologies if my question seems a bit unclear. I am struggling to display data from MongoDB in a Flask template. The code I have currently isn't working as expected. Here's what I've attempted so far: (I tried to get creative) @app.route(&apo ...

Loading images in advance with AJAX for enhanced AJAX performance

My website is structured in a sequential manner, where page1.html leads to page2.html and so on. I am looking to preload some images from the third page onto the second page. After searching, I came across this amazing code snippet: $.ajax({ url ...

"The presence of the <textarea> element is causing disruptions to the

I'm currently attempting to insert infusionsoft contact form code into a CodeBlock within the Avada theme. However, I've encountered an issue with a textarea field in the form that contains the following code: <div class="infusion-field"> ...

Expanding and collapsing Javascript accordion when a new tab is opened

Is there a way to prevent previously opened accordions from remaining open when opening another accordion? Any help on fixing this issue would be greatly appreciated. Thank you! let acc = document.getElementsByClassName('ac-btn'); let i; fo ...

Extract information from a database table for presentation as simple text

I am looking to extract information from each row and display it as plain text on the same page within a paragraph. Here is an example table for reference: <table> <thead> <tr> <th class="a header">A</th ...

Combining Blazor with Bootstrap for seamless form validation

After gaining some experience with Razor, I decided to explore Blazor. However, I encountered a familiar challenge - integrating validation with Bootstrap. The issue lies in the mismatch between Blazor's validation result classes and those of Bootstra ...

Trouble submitting lengthy text in HTML form

Creating a simple HTML form <form method="post" action="process.php"> <label>First Name</label> <input type="text" name="first_name" /> <br /> <label>Last Name</label> <input type="text" nam ...

Bootstrap's innovative design for a data grid

I'm currently working on designing a Grid using bootstrap 3. https://i.sstatic.net/DZzcq.png My tools include html5, css, and bootstrap integrated with React.js. The main feature of my project is a data grid (specifically, a Fixed Data Table). ...

Converting a selection of checkboxes from HTML to PHP code

Having trouble passing multiple checkbox values from HTML to PHP. Here is the test.php : <!DOCTYPE html> <html lang="fr"> <meta charset="utf-8"> <head> <title>Test Multi CheckBox</title> </head&g ...