CSS Scroll snap not snapping on to sections
Asked Answered
D

1

1

I could not figure out what is breaking scroll-snapping here. I have checked lots of other topics on SO but none of them solved this one. I have also tried changing overflow, height/width properties on both the parent and the child elements.

html,
body {
  height: 100%;
  width: 100%;
  max-width: 100%;
}

body {
  z-index: 0;
  scroll-behavior: smooth;
  transition: background-color 1s ease;
  overflow-x: hidden;
}

main {
  display: flex;
  flex-direction: column;
  width: 100vw;
  margin: 0 auto;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  -webkit-overflow-scrolling: touch;
}

section {
  max-height: 100vh;
  height: 100vh;
  width: 90vw;
  border: 1px solid red;
  margin: 0 auto;
  scroll-snap-align: start;
}
<main dir="ltr">
  <section>
    <h1>content1</h1>
  </section>
  <section>
    <h1>content2</h1>
  </section>
</main>
Dib answered 12/5, 2020 at 18:13 Comment(0)
M
2

Here it is slightly simplified for clarity's sake. The main change is reverting the flex-direction back to row, adding a flex-wrap: wrap property, and making the main container height: 100%.

In your original code the main container was simply growing to the height of the flex items, triggering an overflow on the body and not on that container.

Original solution with flex-direction: row:

* {
  padding: 0;
  margin: 0;
}

html,
body {
  height: 100%;
  width: 100%;
}

body {
  scroll-behavior: smooth;
}

main {
  display: flex;
  flex-wrap: wrap;
  width: 100vw;
  height: 100%;
  overflow: auto;
  scroll-snap-type: y mandatory;
}

section {
  height: 100vh;
  width: 90vw;
  border: 1px solid red;
  margin: 0 auto;
}

main section {
  scroll-snap-align: start;
}
<head>
</head>

<body>
  <main dir="ltr">
    <section>
      <h1>content1</h1>
    </section>
    <section>
      <h1>content2</h1>
    </section>
  </main>
</body>

</html>

Edit:

On closer inspection I've realised that it's not actually necessary to change the flex-direction if you give the flex items a flex-basis instead. By default flex-box will try to fit its children inside it's own height so you need to specify their starting size. You also need to provide flex-grow and flex-shrink which control how the element resizes relative to the container.

* {
  padding: 0;
  margin: 0;
}

html,
body {
  height: 100%;
  width: 100%;
}

body {
  scroll-behavior: smooth;
}

main {
  display: flex;
  flex-direction: column;
  width: 100vw;
  height: 100%;
  overflow: auto;
  scroll-snap-type: y mandatory;
}

section {
  flex-basis: 100vh;
  flex-grow: 1;
  flex-shrink: 0;
  width: 90vw;
  border: 1px solid red;
  margin: 0 auto;
}

main section {
  scroll-snap-align: start;
}
<html>
<body>
  <main dir="ltr">
    <section>
      <h1>content1</h1>
    </section>
    <section>
      <h1>content2</h1>
    </section>
  </main>
</body>
</html>

It's another way of achieving the same essential result; that the container will overflow and trigger the scroll behaviour.

Moyer answered 12/5, 2020 at 18:33 Comment(8)
Incredible. Thank you! Why does flex: row break snapping?Millard
Sorry, I don't quite understand your question, flex: row is not a valid CSS property.Moyer
I am sorry, I meant flex-direction: column. Also, I'd like to ask one more question. I had some scroll/position based stuff on JS that depends on window.scrollY, window.innerHeight and document.body.scrollTop. Now that viewport height is 100% at all times these events are not firing. How can I fix this while keeping scroll snapping? This is why I did not change try changing the height in the first place because it broke JS events.Millard
I've updated my answer - it's not actually about the direction of flow but how you force the container to overflow. As for fixing the JS, you could do two things, either target the scroll of the specific element which is scrolling instead of body or move the flex container onto the body (I'd recommend the first).Moyer
I could get neither of these to work with scroll events. Can you look at this fiddle and try to get it working? jsfiddle.net/q9s3pkLx/1Millard
Remember you're not scrolling on the body anymore, you have to target the content that is actually scrolling: jsfiddle.net/cztsxm0q - hope this helps!Moyer
Thank you. I tested these (targeted main directly like you did) locally on my project before asking you again and but somehow the very same code works only on jsfiddle, I don't know why. I could not get it to work on my project. Well, I will tinker with it. Thank you again for helping me and have a nice day!Millard
Update: It has completely gone over my head when you said "target main", I thought you meant target "target main inside the listener". I was still adding that event listener to the window, not main... It works fine nowMillard

© 2022 - 2024 — McMap. All rights reserved.