Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Css scroll-snap bug iOS 10

I noticed a strange bug in iOS 10 with the CSS scroll-snap properties.

Here's my css:

#springBoard{
    height: 100%;
    width: 100%;
    font-size: 0px;
    white-space: nowrap;
    overflow: scroll;
    -webkit-overflow-scrolling: touch;
    -webkit-scroll-snap-type: mandatory;
    -webkit-scroll-snap-points-x: repeat(100%);
}

section{
    display: inline-block;
    width: 100%;
    height: 100%;
    vertical-align: top;
    font-size: 16px;
}

If I programmatically scroll to a snap-point and then change the content inside the scroll-snap container, the nav snaps back to the first snap-point.

// Programatically scroll the scroll-snap container 
$("#springBoard")[0].scrollLeft = 320

It doesn't seem to be related to the way I trigger the scrolling. All these scrolling methods produce the same result:

$("#springBoard")[0].scrollLeft = 320
$("#springBoard").animate({scrollLeft: 320}, 1)
$("#springBoard > section:eq(1)")[0].scrollIntoView()
window.location.hash = "sectionId"
  1. The bug doesn't happen when scrolling manually (See @maxime's comment below).
  2. It's present since version 10.3.2 of iOS.
  3. Don't know if it's fixed in iOS 11.

I spent a couple of days trying to solve the issue but without success so far.

Here's a stripped-down example of my nav:

Codepen Demo

Does anyone know a way around this stupid bug?

like image 795
Etienne Martin Avatar asked Jul 19 '17 18:07

Etienne Martin


1 Answers

I created my own HORIZONTAL jquery scroll-snap which gets triggered on page load, window resize, and scroll of the container - this prevents any need for css:

$(".container").scroll(function() {

The following if/else statement is if you plan to change the object's width depending on the width of the page. If not, you can just delete it and set var width = your default width

   var width = 1; //100% - default value / small screen
    if(window.innerWidth >= 993) //large screen
         width = 1/4; //25%
    else if(window.innerWidth >= 601) //medium screen
        width = 1/3; //33.333%

    var containerWidth = $(".container").width();
    var sectionWidth = containerWidth * width; //gets the length of each section

    var currentScroll = $(".container").scrollLeft();
    var farOff = currentScroll % sectionWidth; //determines how far user is from the beginning of the current section
    if(farOff == 0) //just for efficiency
        return;

    clearTimeout($.data(this, 'scrollTimer'));

    $.data(this, 'scrollTimer', setTimeout(function() {
        if(farOff >= sectionWidth/2) //scroll-snaps to next section
            $(".container").animate({
                scrollLeft: (currentScroll + sectionWidth - farOff),
            });
        else //scroll-snaps to previous section
            $(".container").animate({
                scrollLeft: (currentScroll - farOff),
            });
    }, 550));
});

Below is the CSS that goes along with my scroll snap example

div.container{
    overflow-x: scroll;
    -webkit-overflow-scrolling: touch;
    -o-overflow-scrolling: scroll;
    -moz-overflow-scrolling: scroll;
    overflow-y: hidden;
    white-space: nowrap !important;
}
.container section{
    display: inline-block;
    padding: 10px;
    width:100%;
    vertical-align: top;
    margin-bottom: 10px;
}
.container > section > div{
    overflow: initial;
    white-space: normal;
}
@media (min-width: 601px){ /* medium */
    .container section{
        width: 33.33333333%;
    }
}
@media (min-width: 952px){ /* large */
    .container section{
        width: 25%;
    }
}
like image 95
David_Gracias Avatar answered Nov 10 '22 23:11

David_Gracias