CSS scroll snap triggering on something other than scroll - css

The bounty expires in 4 days. Answers to this question are eligible for a +50 reputation bounty.
rbhalla wants to draw more attention to this question.
Here is an example: https://codesandbox.io/s/spring-wood-0bikbb?file=/src/styles.css
To replicate:
focus the input at the top
scroll all the way to the bottom, click on an empty space
notice that the screen jumps back up to the top to the snap target
What I've determined seems to be contributing to this:
The scroll window being ontop of a space with no snap target in proximity
The input width being resized on focus/blur
What the demo does:
On focus/blur of the input, a class is attached/removed from the container element.
That class changes the width of the container
The input container is the only element with scroll-snap-align set.
Question: what is causing this jump to occur, and how do I stop it?

It seems you have stumbled onto a chromium bug. Nowadays they are a little less common, but still not that rare. We tend to find them in the edge-cases of new(-ish) features and there is no other choice but to work around them.
Here's the minimal reproducible example, which replicates your issue in Chrome, but not in Safari nor Firefox (macOS).
.main {
scroll-snap-type: y proximity;
height: 100vh;
overflow-y: scroll;
background: green;
}
.spacer {
height: 200%;
}
input {
width: 25%;
scroll-snap-align: start;
}
input:focus {
width: 50%;
}
<div class="main">
<input type="text" />
<div class="spacer"></div>
</div>
If you want to be a good citizen of the web, your next step would be to check on the chromium bug tracker whether that bug has been reported already or not, and if needed file an issue yourself https://bugs.chromium.org/p/chromium/issues/list
As for your practical case of what to do to avoid it? As long as it is not fixed you'll need to find a workaround. This could include (not tested but likely to work):
only enabling scroll snapping under certain conditions (pretty standard workaround for other scroll-snap related issues chrome has had for the past few years)
force the scroll position on input blur
don't resize the input
add a snap target below the input
change your design / functional requirements

Related

Can I achieve CSS scroll snapping without making non-snapping elements unreachable?

I'm trying to use scroll snapping via the CSS scroll-snap-type and scroll-snap-align properties, but no matter what I do I end up making non-snapping elements unreachable.
In the example below, for example. the paragraphs (<p>) are all reachable and even snapping just fine, but the header (<h1>) becomes unreachable because when I try to scroll up to view it, I just get snapped back down to the first paragraph beneath it.
html {
scroll-snap-type: y mandatory;
height: 100vh;
overflow: scroll;
}
p {
background: pink;
padding: 3rem 4rem;
scroll-snap-align: start;
}
<h1>THIS IS UNREACHABLE</h1>
<p>1.1</p>
<p>1.2</p>
<p>1.3</p>
<p>1.4</p>
<p>1.5</p>
<p>1.6</p>
<p>2.1</p>
<p>2.2</p>
<p>2.3</p>
<p>2.4</p>
<p>2.5</p>
<p>2.6</p>
<p>3.1</p>
<p>3.2</p>
<p>3.3</p>
<p>3.4</p>
<p>3.5</p>
<p>3.6</p>
My question: Is there a way to achieve CSS scroll snapping without making non-snapping elements unreachable?
further notes:
You'll probably notice that I'm using scroll-snap-type on the <html> tag rather than using a container, which is apparently more typical. That's because using a container makes things even worse, introducing multiple scrollbars and confusion over whether the container or the <body> is being scrolled. Also, for the scroll-snapping design I'm trying to achieve to function, I would need to force the container itself to snap to the top of the viewport, which just brings us back to using <body>, or, since that simply doesn't work (don't know why), <html>.
It's not really possible. Because the scroll snapping API wasn't meant to be used with mixed non-snapping elements. I ran into the same issue myself, hence why I ended up on your entry. One thing that helps a little is to use proximity instead of mandatory on the html element.
Obviously that yields a different scroll feel that you might be going for. And it has its own shares of issues; such as when you refresh the page it might still scroll down to the first <p> element because it happens to be close enough for the proximity value to trigger. To kind of avoid this you can unset snapping on the first and last child with pseudo selectors.
In the demo below it looks like it might work, but this is where things go from bad to really bad. Let's say you want to have some content below the <p>'s that also doesn't snap. If you resize the window, while at the very bottom you'll notice that the window jumps back up to the last registered snap point. Same thing will happen to any layout shifts such as opening and closing an accordion, or browsing on a mobile device with a shifting url bar. Not ideal.
So in terms of viability; mixing snap enabled together with non-snapping elements is a rabbit hole of despair, dont do it.
html {
scroll-snap-type: y proximity;
height: 100vh;
overflow: scroll;
}
p:first-of-type {
scroll-snap-align: unset;
}
p {
background: pink;
padding: 3rem 4rem;
scroll-snap-align: start;
}
h2 { height: 150vh; }
<h1>THIS IS UNREACHABLE</h1>
<p>1.1</p>
<p>1.2</p>
<p>1.3</p>
<p>1.4</p>
<p>1.5</p>
<p>1.6</p>
<p>2.1</p>
<p>2.2</p>
<p>2.3</p>
<p>2.4</p>
<p>2.5</p>
<p>2.6</p>
<p>3.1</p>
<p>3.2</p>
<p>3.3</p>
<p>3.4</p>
<p>3.5</p>
<p>3.6</p>
<h2>content below</h2>

Setting overflow on container breaks translateZ

I want a cool scrolling effect on my website like this:
There are two background images that scroll much slower than the rest of the page, divided by another element with a higher z-index value and unmodified scroll speed.
I have managed to do so in Firefox using 3d transform and overflow: hidden (as you can see, the two background images do not overlap). Here's the significant part of the code:
HTML:
<div class="container-container">
<div class="container">
<div class="slow-scroll">
<img src="...">
</div>
</div>
</div>
CSS:
.container-container {
perspective: 100px;
}
.container {
transform-style: preserve-3d;
overflow: hidden;
}
.slow-scroll {
transform: translateZ(-900px) scale(10.5);
}
My intention was to do the same as in the GIF - have a container with regular scroll speed that would contain the slower scrolling background, so that the overflow could be hidden, thus making it impossible for the two background images to overlap.
However, this doesn't work for Chrome or Microsoft Edge - the background images act as if the translateZ() value was absent (the scale still works normally though). This happens if I set overflow: hidden to any value except initial, revert, or unset.
Can this be fixed? Is there a workaround?
Note: I have looked around and saw this question, but it's outdated and the accepted answer does not work for me.
Based on your description, and the sample images provided, I think what you're looking for is a parallax scrolling effect. Simply refer to this tutorial below, which contains a simple example. I think it will help you.
How TO - Parallax Scrolling
Edit:
And according to your description, you mentioned that translateZ() not work in Edge, I created a simple demo and tested it, and I found it works normally in Edge(version 97.0.1072.55). I think maybe there is some problem with the code, such as this line:
transform: transform: translateZ(-900px) scale(10.5);
And if you want to implement this requirement, you could try to create multiple layers and set different translateZ() values for them. Simply refer to this example: Pure CSS Parallax Websites - Demo3

Firefox bug with float and nowrap?

In this fiddle, why is B falling out of the yellow container?
http://jsfiddle.net/en7qfto1/
It does not happen in Chromium.
Is that a Firefox bug?
This is the code:
<style>
.a, .b
{
display: inline-block;
background: lightblue;
}
.b
{
float: right;
}
.c
{
background: yellow;
white-space: nowrap;
}
</style>
<div class=c>
<a class=a>A</a>
<a class=b>B</a>
</div>
Yes, this is a bug. You can find the Bugzilla ticket here.
David Baron points out the cause, which indicates in the code itself that this is due to a known limitation:
The cause is this code in nsLineLayout::ReflowFrame:
if (psd->mNoWrap) {
// If we place floats after inline content where there's
// no break opportunity, we don't know how much additional
// width is required for the non-breaking content after the float,
// so we can't know whether the float plus that content will fit
// on the line. So for now, don't place floats after inline
// content where there's no break opportunity. This is incorrect
// but hopefully rare. Fixing it will require significant
// restructuring of line layout.
// We might as well allow zero-width floats to be placed, though.
availableWidth = 0;
}
I wonder whether the right thing to do is:
not manipulate the available width at all, or
make the available width infinite, since the nowrap content is never going to wrap around the float anyway
(In theory, the correct solution is not to try placing the float until the following break opportunity. I wonder if other browsers do that.)
I actually think this is quite doable; we already defer layout of floats in mBelowCurrentLineFloats; we just need to do something similar and notify line layout at break opportunities. It's far from trivial, though. (We also need to place the float immediately if we're currently at a break opportunity... I think.)
I too wonder if that is what other browsers do (Chrome and IE behave the same, placing the float on the same line as the inline-block). Unfortunately I don't fully understand the interaction between floats and line breaks, so I can't comment further.
I ran into the same bug, and for me the best workaround was to use an equivalent flexbox layout: http://jsfiddle.net/nquyefaq/.
It behaves the same on Chrome, Firefox and IE11:
Both items render on the same line, with inline-block type behaviour.
The second item is right-aligned. (Instead of float: right it uses margin-left: auto.)
If the items have fixed width (mine do), the second item drops down onto the next line if the container becomes too narrow.
I hope this helps someone else looking for a workaround.

Google Chrome not respecting z-index

As per the title, it seems only Chrome isn't playign along. Note that form fields cannot be clicked on which are on the left portion of the screen. This only occurs on some pages (such as the Contact page). It appears that the #left_outer div is overlaying the content. When I edit the css via Firebug or Chrome's dev toools, it works, when I edit the actual css and refresh, it does not.
Any ideas?
LINK:
Thanks!
Usually when you have set the z-index property, but things aren't working as you might expect, it is related to the position attribute.
In order for z-index to work properly, the element needs to be "positioned". This means that it must have the position attribute set to one of absolute, relative, or fixed.
Note that your element will also be positioned relative to the first ancestor that is positioned if you use position: absolute and top, left, right, bottom, etc.
Without a link to look at, it's a bit tough to see what the problem might be.
Do you have a z-index: -1; anywhere (a negative number is the key here, doesn't matter the number)?
I have found in the past this renders the container void from being interacted with.
Good luck!
Markt's answer (see first answer) is great and this is the "by definition" of the z-index property.
Chrome's specific issue are usually related to the overflow property from the top container bottom.
So, for the following:
<div class="first-container">...</div>
<div class="second-container">
<div ...>
<div class="fixed-div> some text</div>
<... /div>
</div>
And styles:
.first-container {
position:relative;
z-index: 100;
width: 100%;
height: 10%;
}
.second-container {
position:relative;
z-index: 1000;
width: 100%;
height: 90%;
overflow: auto;
}
.fixed-div {
position: fixed;
z-index: 10000;
height: 110%;
}
the following actually happens (Chrome only, firefox works as expected)
The 'fixed-div' is behind the 'first-container', even though both 'fixed-div' and its container's ('second-container') z-index value is greater than 'first-container'.
The reason for this is Chrome always enforce boundaries within a container that enforces overflow even though one of its successors might have a fixed position.
I guess you can find a twisted logic for that... I can't - since the only reason for using fixed position is to enable 'on-top-of-everything' behavior.
So bug it is...
I had a weird issue with zIndex on Chrome and I kept fiddling with the position attribute to see if anything worked. But, it didn't. Turns out, in my case, the issue was with the transform attribute. So, if you have a transform attribute in place, disable it and it should be fine. Other browsers work fine with stuff like that, but Chrome seems to play it differently.
Hope this helped you.
Google Chrome to 84.0.4147.135 (Official Build) (64-bit) 2020-02-22.
Since my last update, CSS element z-index is broken in Chrome.
Chrome added "z-index: 1;" to the BODY element.
It now wrongly displays all z-index: ?; values in the BODY child elements.
Setting the position, z-index of BODY does not solve the problem.
Changing z-index values of child elements that were already correct does not help.
I hope this issue will be fixed, it is only broken since I updated Chrome.
Chrome 84.0.4147.135 bug on www.eatme.pro/music - screen smaller than 500 px - push play - appearing bottom bar #lblBottomBarLink with z-index 5 is displayed under menu with z-index 2
(see image)
image eatme.pro/music in Chrome 84.0.4147.135 with z-index 5 under z-index 2
I know this is now resolved but posted solution didn't work for me. Here is what resolved my problem:
<act:AutoCompleteExtender ID="ace" runat="server" OnClientShown="clientShown">
</act:AutoCompleteExtender>
<script language="javascript" type="text/javascript">
function clientShown(ctl, args) {
ctl._completionListElement.style.zIndex = 99999;
}
</script>

How can a URL fragment affect a CSS layout?

Compare these 3 URLs (look at the top navigation bar in each case):
http://fast.kirkdesigns.co.uk/blog
as above but with the url fragment #navigation
as above but with the url fragment #node-2655
Note, that the only difference is the URL fragment on the end.
The first two pages display absolutely fine (in Firefox at least). It's the third one where the problem lies. The fragment #node-2655 pushes the top navbar off the top of the screen. When you then scroll back up to the top of the page, the navbar has been cut in half. This happens when using any URL fragment that causes the navbar to be out of the initial viewport when the page is first loaded.
So, how can using a url fragment affect the css layout like this?!
THE SOLUTION:
as suggested below, removing the overflow: hidden on the container element that held the navbar fixed the problem. I'd love to understand why though!
Remove the overflow:hidden on #main in css_75afd7072eaf4096aaebf60674218e31.css
I'd say it's a rendering bug in FireFox as it's fine in Opera. There shouldn't be anyway an anchor would change the CSS like you say (unless you are using jQuery or something).
I am having this problem too, and think I can see what is happening.
The "column" block with the massive (5678 pixel) margin and padding makes that block very tall. In browsers other than Firefox, the positive and negative values cancel each other out, but FF really does make it that tall - kind of.
FF also knows the two cancel each other out, but seems to look at the 5678px padding and decides the column block is poking out the bottom of the #wrapper block. This is overflow - and with overflow set to auto on #wrapper, you see the true size of #wrapper with a scroll-bar down the side.
With overflow set to hidden, FF takes away the scrollbar, but still seems to scroll the contents of #wrapper so that the item the fragment points to is at the top of the page. This is normal behaviour for fragment links in scrollable blocks, but since there is no scrollbar, you cannot scroll the content back down again, hence it looks like the layout has been effected by the fragment.
So in short, I suspect that FF is operating an invisible scrollbar in this example. That could be considered a bug, but it is probably correct behaviour. Being able to scroll the content up and down inside a non-overflowed fixed-sized block using URL fragments, is a technique that can be used effectively to implement image "sliders" that work even in the absence of JavaScript.
Hope that helps. This has been puzzling me for years, and this explanation suddenly struck me out the blue. My current workaround for this is to use jQuery "scroll to" plugin to scroll the whole page down to the fragment, as this seems to prevent the contents of #wrapper from scrolling internally.
You can also take "display: hidden" off #wrapper, but your page then ends up half a mile long.
I'll just point out that there may be some weird inheritance from the 30+ stylesheets linked to in the head. There may not, either, and it's probably a rendering bug (possibly related to :target styling) that Dan suggested. I just felt it worth pointing out that if you've got more than thirty stylesheets, you likely to start seeing some weirdness, whatever else might happens.
The reason is the column with the large padding has expanded it's container, but the expansion is then hidden but overflow:hidden; but with the use of the fragment it is being scrolled into the position of the fragment, effectively chopping off anything above that. You can use javascript and set scrollTop to 0 and it scroll it back to the normal position.
Basically a wierd edge case which browsers do not seem to handle very well.
Sorry this isn't an "answer," tho it is a response to the other comments here. This problem is just flabbergasting. It is very easy to isolate (i.e., has nothing to do with number of stylesheets), and doesn't have a proper "solution," as there is no way to achieve the desired rendering.
<!DOCTYPE html>
<html>
<head>
<style>
#container {
margin: 1em auto;
width: 40em;
}
#wrapper {
overflow: hidden;
position: relative;
}
#c1 {background-color: #aaf;}
#c2 {background-color: #ccf;}
.column {
float: left;
margin-bottom: -5678px;
padding-bottom: 5678px;
width: 50%;
}
#footer {
background-color: #eee;
padding: 1px;
text-align: center;
}
p {margin: 1em;}
</style>
</head>
<body>
<div id="container">
<div id="wrapper">
<div id="c1" class="column">
<p>This is some content in a short column. We would need some Javascript to change its height if we wanted a different background color for each column to stretch the full height of the respective columns...or we can use large padding together with an equal negative margin.</p>
<ul>
<li>Jump to P1</li>
<li>Jump to P2</li>
<li>Jump to P3</li>
</ul>
</div>
<div id="c2" class="column">
<p id="p1">The desired effect is to have the height of the two columns appear the same. We use 'overflow:hidden' on the containing div (#wrapper) to wrap it around the floated columns.</p>
<p id="p2">These paragraphs have fragment identifiers. Problem comes in when clicking one of the links on the left. Instead of scrolling just the page, the browser scrolls the div with 'overflow:hidden' so the target is at the top. It does this even if the target is already visible.</p>
<p id="p3">Opera does not exhibit this behavior. This occurs in Chrome/Safari, Firefox, and IE. (Interestingly, IE also works as expected if we completely remove the DOCTYPE declaration.)</p>
</div>
</div>
<div id="footer">
<p>Footer stuff.</p>
<p>To see why 'overflow: hidden' (or any other piece of the CSS) is needed, just try disabling it.</p>
</div>
</div>
</body>
</html>
Just as a side-note, the above technique is generally used to provide flexible-width mulit-column layouts. This is probably becoming less important these days as fixed-width layouts are becoming a lot more comment - browsers are able to magnify the web page to see small text, and fixed-width makes it a lot easier to control the typography of a page, e.g. set the width (in ems) to display the ideal nine words per line regardless of what font size and magnification the user chooses.
Sorry if that does not sound like an answer, but it is basically suggesting to discard this old model and consider moving to fixed-width columns (which is a whole new subject).
I was able to solve this with some javascript to scroll the body to the position the overflow hidden element was scrolled to.
setTimeout(() => {
let intendedScroll = document.getElementById("fragmentfix").scrollTop;
document.getElementById("fragmentfix").scrollTop = 0;
window.scrollTo(0, intendedScroll);
}, 0)

Resources