preface

Reference: Bouncing On Your Websites; reference: Bouncing On Your Websites;

takeaway

In this article, we introduce bouncing effect and implementation of spring scroll on different browsers. We also review several common solutions on the web and introduce the recent implementation of CSS property Overscroll Behavior. Hopefully this will help you build and design pages with fixed elements

Scroll bouncing

Spring effects (also known as sliding Rubber band effects or elastic rolling) often occur in the following scenarios:

  1. When scrolling to the top or bottom of a page or HTML element, you will briefly see white space appear before the page or element returns to the top/bottom (i.e. before you release your finger or mouse).
  2. The same effect can be seen in CSS Scraghed-snapping between elements. This article focuses on the first case, namely, the bouncing of the Scroll port when it reaches its scrolling boundary. An in-depth understanding of Scroll bouncing helps us determine how web pages are constructed and how pages are scrolled.

background

Spring effects are less pleasant when you don’t want to see fixed elements moving around the page. For example, we want a fixed header and footer on the page, or we want a fixed menu, or we want to capture the exact position of the page during scrolling, or we don’t want extra scrolling at the top or bottom. At this point we need to look at what solutions to solve this kind of page top/bottom spring effect.

Scenario review

Let’s say we have a page with a fixed, immovable footer at the bottom, and the rest of the page can scroll. It looks like this:It works as expected on Firefox and other browsers that don’t have touch screens and touchpads, but when we use Chrome on the MAC, things are a little different when we scroll to the bottom of the touchpad.

Even with the CSS with the footer set to Fixed =>bottom, the rubber band effect is a bit unexpected. Let’s look at the meanings of position:fixed According to the CSS 2.1 Specification, when a “box” (in this case, the dark blue footer) is fixed, It is “fixed with respect to the viewport and does not move when scrolled.” According to CSS 2.1 specification: When a box(footer, obviously) is set to fixed, it is positioned according to viewPort and does not move during scrolling.

Obviously the above effect is not expected. To complete the article, I’ve tried out the effects of Edge on mobile, Safari on mobile, and Safari on desktop, and it’s true that they behave differently on Firefox and Chrome. Developing the same effect on different platforms can be challenging.

The solution

For us, the first idea would be simple and quick, so in this case, of course, the first choice is CSS alone. So choose the following ways to try it out. The test browsers included Chrome, Firefox, Safari on Windows 10 and MAC, as well as Safari on Edge and Mobile, all with the latest versions of 2018. The page structure is as follows:

<html>
  <body>
    <div class="body-container">
      <div class="color-picker-main-container"> 
      </div>
    </div>
    <footer>
    </footer>
</body>    
Copy the code

Only CSS, HTML to solve

First, the way of absolute positioning and relative positioning

Use absolute to position the footer, then the HTML positions height100% relative to height so that the footer is always fixed below, and the height of the content is 100% minus the height of the footer. You can also set padding-bottom instead of calc, and set body-containe to 100% to prevent footer repetition. Language is pale, look at the code is finished:

html {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}

body {
  width: 100%;
  margin: 0;
  font-family: sans-serif;
  height: 100%;
  overflow: hidden;
}

.body-container {
  height: calc(100% - 100px);
  overflow: auto;
}

.color-picker-main-container {
  width: 100%;
  font-size: 22px;
  padding-bottom: 10px;
}

footer {
  position: absolute;
  bottom: 0;
  height: 100px;
  width: 100%;
}
Copy the code

This way is almost the same as the original fixed way. The difference is that this method is no longer the entire page but the content, not the footer. The biggest problem with this approach is that on safari on mobile, not only content, but footer slides along with it… It’s a disaster when it slides too fast. The following figure

In addition, another undesirable situation also occurs, when trying to slide around, it is found that the sliding performance is a little poor at this time. Because we set the height of the sliding container to 100% of its own, it obstructs the momentum based scrolling on ios. I don’t have a good language to translate the momentum based scrolling. Damping sliding bar for short is a function added on mobile devices to improve the sliding performance of the page. Obviously, when you touch the surface of the device with your finger, the page itself will start to slide, and when the finger stops sliding, the page will slide for a while. For more information, please refer to. I definitely want this effect, so stay away from setting the slider height100%.

Before we move on, let’s slow down and think about our current state. There was a problem with rubber band in the original fixed positioning. If it was changed to absolute+relative above, there was no damping sliding. If you want to damp sliding, the height of the content section cannot be set to 100%. Can we not explicitly set height to 100%?

html {
  width: 100%;
  position: fixed;
  overflow: hidden;
}

body {
  width: 100%;
  margin: 0;
  font-family: sans-serif;
  position: fixed;
  overflow: hidden;
}

.body-container {
  width: 100vw;
  height: calc(100vh - 100px);
  overflow-y: auto;
  // Use momentum-based scrolling on WebKit-based touch devices
  -webkit-overflow-scrolling: touch;
}

.color-picker-main-container {
  width: 100%;
  font-size: 22px;
  padding-bottom: 10px;
}

footer {
  position: fixed;
  bottom: 0;
  height: 100px;
  width: 100%;
}

Copy the code

Set HTML and body to fixed, overflow: hidden. Footer is also fixed.

Set the height to 100vh-footer in the body container content area where you want to scroll.

-webkit-overflow-scrolling: touch; Enable damped sliding support.

So what’s going to happen. Chrome and Firefox on MAC have the same representation as the previous method, but the advantage of this method is that it no longer requires 100% height,

So momentum based scrolling works fine, but in Safari, the footer is missing…

On iOS Safari, the footer is shorter and has an extra gap at the bottom. Similarly, when scrolling to the bottom, the ability to scroll the page disappears.

-webkit-overflow-scrolling: touch; Adds the ability to momentum-based scrolling to the specified element. However, this attribute is identified as non-standard in MDN and compatibility considerations are required, so it will have to be discarded.

The alternative is as follows:

html {
  position: fixed;
  height: 100%;
  overflow: hidden;
}

body {
  font-family: sans-serif;
  margin: 0;
  width: 100vw; 
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

.color-picker-main-container {
  width: 100%;
  font-size: 22px;
  padding-bottom: 110px;
}

footer {
  position: fixed;
}

Copy the code

This works well on different desktop browsers, with damped sliding, the footer fixed and not followed. The downside of this approach is that on iOS Safari you can see the footer wobble slightly and you can see the content under the footer when you swipe.

Using javascript

Since the above way are some flaws, so we still try JS to solve it. First of all, I don’t recommend it and I recommend avoiding it as much as possible. Based on the author’s experience, there should be a more elegant and concise HTML + CSS approach. However, a lot of time has been spent trying to solve this problem, and it doesn’t hurt to see if there’s a better way to use JS.

One way to avoid sliding springs is to prevent window or Document touchMove or TouchStart events. The idea is to block tocuch events for the outer window and only allow touch for the Content part. The code is as follows:

// Prevents window from moving on touch on older browsers.
window.addEventListener('touchmove'.function (event) {
  event.preventDefault()
}, false)

// Allows content to move on touch.
document.querySelector('.body-container').addEventListener('touchmove'.function (event) {
  event.stopPropagation()
}, false)
Copy the code

I’ve tried many ways to make the swipe behave well, blocking Widow’s Touchmove is no different than blocking Document, I’ve also tried using TouchStart and TouchMove to control the swipe, but there’s no difference either. It turns out that the event.preventdefault () should not be used in this way for performance reasons and should be set to false as passive.

// Prevents window from moving on touch on newer browsers.
window.addEventListener('touchmove'.function (event) {
  event.preventDefault()
}, {passive: false})
Copy the code

tool

Another way to help yourself is to use iNoBounce, a library designed to solve the spring effect of web apps sliding on ios. SRC/app/webkit-overflow-scrolling if necessary. In addition, the concise method I mentioned at the end is similar to this one, so compare the two.

Overscroll Behavior

After trying so many solutions, I found a property of CSS called overscroll behavior, which was implemented in Chrome 63 in December 2017 and Firefox 59 in March 2018. As defined by MDN: allows you to control the behavior of the browser’s slide overflow – the behavior that occurs when the edge of the scroll area is reached. This is the final solution. The only thing you need to do is set the body behavior to overscroll: None and set the footer to fixed. Momentum based scrolling is acceptable for the entire page compared to no FOter. More objectively, Edge is in development and has a long way to go.

conclusion

Refer to the article

  1. Momentum Scrolling on iOS Overflow Elements
  2. Scroll Bouncing On Your Websites
  3. MOMENTUM SCROLLING USING JQUERY

Thanks again to the original author William Lim, who provided a rich solution to sliding rubber band effects. Some translation is not in place, please point out the details of the original asynchronous