How to force Safari to repaint position:fixed elements on scroll?
Asked Answered
E

3

16

I am having issues with Safari on desktop and mobile devices being very slow to repaint elements with position:fixed when the user scrolls.

The elements with position:fixed that safari is having difficulty with are the #intro, and the footer elements of .portfolio-item .expanded-content. The #intro on scroll doesn't necessarily repaint to the correct z-index (it is supposed to be behind other elements as the user scrolls). The footer elements on mobile don't stay in a fixed position above the content on scroll on iOS safari. Scrolling on iOS safari is jaggy (iOS chrome, however, is fluid and everything is working as expected).

I did a fiddle where I stripped out all the images, fonts, and JS, and lo and behold, safari has no trouble with repainting position:fixed elements on scroll.

Since this is a portfolio site, stripping out my images is obviously not an option. I was really hoping to make this a true one-page site, and not use AJAX or anything to load content on demand. Am I asking too much of safari to have that many elements and be able to repaint the elements with position:fixed on scroll? Chrome and FF don't seem to have a problem with it; neither do IE9, 10, 11.

I am not completely certain it's a repainting issue, but you can see in the video below that if I am forcing Safari to repaint by triggering an event that is not scroll, like a mouseover event, it repaints, and puts that position:fixed element in the z-index I specified in my stylesheet. So that fact, combined with the fiddle working just fine, is why I'm assuming it's a repainting issue, and not an issue with my code.

I'm hoping to figure out a way to fix this issue without resorting to more JS or dynamically loaded content, to maintain the same design (not abandon the idea of using position:fixed or a fluid layout just because one browser is having issues with it) and to try to keep performance quick and smooth. I thought about using JS each time the user scrolled to force safari to repaint, but that seems to me like it would negatively impact performance in all browsers. I could really use some other people's thoughts and perspectives on this.

the site: http://sarahjean.co

the fiddle: http://jsfiddle.net/sjc8611/n9z3W/

the video: https://dl.dropboxusercontent.com/u/24724104/position-fixed-repaint-lag-safari.mov

The html:

    <nav data-scroll-header="" id="main-navigation">
    <ul>
        <li><a href="#work" data-scroll="">Work</a>

        </li>
        <li><a href="#about" data-scroll="">About</a>

        </li>
        <li><a href="#services" data-scroll="">Services</a>

        </li>
        <li><a href="#contact" data-scroll="">Contact</a>

        </li>
    </ul>
</nav>
<div class="header" id="header">Header Content</div>
<section id="intro" class="container">
    <article class="content">
            <h1>Introduction Text</h1>

        <p>Welcome to my super cool portfolio site. Check out how awesome I am. You should totally hire me.</p>
    </article>
</section>
<section id="work" class="container">
    <article class="content">
            <h1>Work</h1>

        <nav id="portfolio-navigation">
            <ul>
                <li><a href="#work1">See My Work 1</a>

                </li>
                <li><a href="#work2">See My Work 2</a>

                </li>
            </ul>
        </nav>
    </article>
    <article id="work1" class="portfolio-item">
        <div class="expanded-content">
                <h2 class="center">Here is some of my work!</h2>

            <p>Lorem ipsum dolor sit amet..</p>
            <footer><a href="#work">Close</a>

            </footer>
        </div>
    </article>
    <article id="work2" class="portfolio-item">
        <div class="expanded-content">
                <h2 class="center">More of my cool work!</h2>

                <h1>Proin Quis Tortor Orci. Etiam At Risus</h1>

            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit..</p>
            <footer><a href="#work">Close</a>

            </footer>
        </div>
    </article>
</section>
<section id="contact" class="container">
    <article class="content">
            <h1>Contact</h1>

        <ul id="contact-list">
            <li>I would include a list of ways to contact me here</li>
            <li>Emails</li>
            <li>Telephones</li>
            <li>The postal services</li>
        </ul>
    </article>
</section>
<section id="services" class="container">
    <article class="content">
         <h1>Services</h1>
        <p>Lorem ipsum dolor sit amet..</p>
    </article>
</section>

the CSS:

body {
    background: #fff8ec;
    margin: 0 auto;
    height: 100%;
}
html {
    font-family: Arial, sans-serif;
    font-size: 14px;
    line-height: 135%;
    color: #4b3d2f;
    height: 100%;
}
h1, h2, h3, h4, h5 {
    font-family: Arial, sans-serif;
}
h1 {
    color: #aba499;
    text-align: center;
    font-size: 2em;
}
.portfolio-item h2 {
    font-size: 1.8em;
}
a, a:link, a:visited {
    color: #c85128;
    text-decoration: none;
}
a:hover {
    color: #4b3d2f;
}
p {
    margin: 1em 0;
    line-height: 135%;
}
img {
    max-width: 100%;
    height: auto;
}
.container {
    width: 100%;
    position: relative;
    background-color: #e5e2de;
    padding: 100px 0;
}
.container > .content {
    width: 80%;
    margin: 0 auto;
    max-width: 800px;
    background-color: transparent;
}
#header {
    background-color: #c85128;
    height: 95%;
    position: relative;
    z-index: 3;
    display: table;
    width: 100%;
}
#intro {
    background-color: transparent;
    position: fixed;
    top: 5%;
    left: 0px;
    height: 25%;
    padding: 5% 0;
    z-index: 0;
}
#intro > .content {
    background-color: #fff8ec;
    width: 70%;
    padding: 5%;
    border-radius: 20px;
    box-shadow: 0px 1px 3px #e5e2de;
}
#work {
    margin-top: 55%;
    background-color: #dedad5;
}
#contact {
    background-color: #d8d3cd;
}
#services {
    background-color: #d1cbc4;
}
#about {
    background-color: #cac4bc;
}
section h1 {
    padding: 50px 0;
}
article .expanded-content h2, article .expanded-content p {
    margin: 50px auto;
}
#main-navigation {
    display: table;
    width: 100%;
    background-color: #aba499;
    position: fixed;
    top: 0;
    left: 0;
    height: 3em;
    overflow: visible;
    z-index: 2;
}
#main-navigation a {
    color: #fff8ec;
    font-family:'NovecentowideBookRegular', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    display: block;
}
#main-navigation a:hover {
    color: #4b3d2f;
    text-shadow: 0em -0.05em 0em #e5e2de;
}
#main-navigation ul {
    display: table-row;
    height: 3em;
    overflow: visible;
}
#main-navigation ul li {
    display: table-cell;
    width: 20%;
    padding: .8em;
    text-align: center;
    vertical-align: middle;
}
.portfolio-item {
    max-height: 0px;
    height: 0px;
    overflow: hidden;
    position: fixed;
    top: 3em;
    left: 0%;
    -webkit-transition: height 700ms ease;
    -moz-transition: height 700ms ease;
    -ms-transition: height 700ms ease;
    -o-transition: height 700ms ease;
    transition: height 700ms ease;
}
#work1:target, #work2:target {
    max-height: 1000px;
    opacity: 1;
    width: 80%;
    height: 70%;
    padding: 5%;
    top: 5%;
    left: 5%;
    background-color: #fff;
    box-shadow: 0px 0px 100px rgba(0, 0, 0, 0.5);
    z-index: 10;
    overflow-y: scroll;
    -webkit-overflow-scrolling: touch;
}
#work1:target .expanded-content, #work2:target .expanded-content {
    max-width: 900px;
    margin: 0 auto;
}
#work1:target .expanded-content footer, #work2:target .expanded-content footer {
    display: block;
    width: 90%;
    text-align: right;
    background-color: #c85128;
    position: fixed;
    top: 5%;
    left: 5%;
    z-index: 11;
}
#work1:target .expanded-content footer a, #work2:target .expanded-content footer a {
    display: block;
    padding: 1em;
    color: #e5e2de;
    height: 1em;
}
Excerpt answered 3/4, 2014 at 16:0 Comment(4)
Would like to help but I'm not seeing the issue you illustrated in the video. Tested in Safari (desktop) 6.1.2, Safari Mobile (iOS 6 on 3G), Chrome 33 (desktop), Chrome iOS. The "Design/Develop Services" box is displaying as it should when I test.Browder
Thanks. I'm testing it on Safari 7.0.3 for desktop and still getting the issue. I've asked a couple people I know to try it on their environments, and they are also experiencing the issue I am. Maybe I should see what the differences between 6.1.2 and 7.0.3 are? EDIT: I just tested it on 6.1.2 on my husband's computer and I'm experiencing the same issue there too.Excerpt
i tired it on safari 5.17 i can't find the issue...Rabble
I just logged in to my cross-browser testing account and tried it on OSX 10.7 and 10.8 on safari 6.1, and I am able to replicate the issue there, so I'm not sure why you two are unable to replicate it when I am seeing it in every Safari test environment I have access to. Is it a difference in the way we are scrolling? Are you swiping/using mouse wheel? Clicking the scrollbar? Are you just clicking the navigation? Is your cursor staying over other elements on the page as you scroll that might trigger a mouseover event?Excerpt
R
12

You're not crazy. I'm having issues with position: fixed elements not repainting either. Haven't found a solution yet.

Edit Found a solution. You can make almost anything repaint by triggering a CSS animation on it that bumps it's size. Here's the snippet I'm using:

.foo
  position: fixed
  &.active
    animation: repaint 1ms

@keyframes repaint
  from
    width: 99.999%
  to
    width: 100%
Ropy answered 9/7, 2014 at 4:29 Comment(6)
Could you explain css style in detail? What does "&.active" mean?Doublejointed
Sorry, it's stylus. It means .foo.activeRopy
Wow, elegant and simple solution. I had a rather complex menu bar, and on iOS I had an issue when the scrollTop needed to remove the fixed class. This approach totally solved the issue for me.Ideologist
What are the side effects with this solution?Wrigley
I don't really know. It's been a while since I used or had need for this.Ropy
and here in 2021, safari still has the problem. But your solution solves itUnmindful
I
4

In case you have no idea what stylus styles are and thus can not use @CorySimmons' solution here is a css version of the code above. Also I had to change some values, clearly the values above those not work in iOS 8

@-webkit-keyframes repaint {

    from {
        width: 99.9%;
    }
    to {
        width: 100%;
    }

}

@-moz-keyframes repaint {

    from {
        width: 99.9%;
    }
    to {
        width: 100%;
    }

}

@keyframes repaint {

    from {
        width: 99.9%;
    }
    to {
        width: 100%;
    }

}

.repaint {
    -webkit-animation: repaint 100ms;
       -moz-animation: repaint 100ms;
        -ms-animation: repaint 100ms;
            animation: repaint 100ms;
}

All you need to do is to give the fixed element a class of .repaint when it needs to be repainted. In my case it was a sticky navigation using jQuery's scrollTop() to add and remove classes from my masthead, so when needed, the jquery function also added .repaint class to my masthead and it solved the issue for me.

Ideologist answered 18/11, 2014 at 12:52 Comment(0)
S
3

I have encountered exactly the same problem in Safari 9.1.

Extending the time that animation executes worked for me in most cases.

@keyframes repaint {
  from {
    width: 99.999%
  }
  to {
    width: 100%
  }
}

.repaint {
  animation: repaint 5000ms;
}

However if the fixed position DOM element was inside the parent that height changed (e.g. parent height can change when new DOM elements are dynamically added), then it didn't work for me, even when extending the animation time to unreasonable values.

My final solution was to drop the animation hack and to force redraw in JS with

$('.repaint').hide().show(0);

as suggested in Force DOM redraw/refresh on Chrome/Mac

I use AngularJS, and to have this hack working in all cases I had to call this .hide().show(0) on each digest loop.

Hack in the form of AngularJS directive:

function ForceRepaintDirective() {
  return {
    restrict: 'EA',
    link: function(scope, $element) {
      scope.$watch(function() {
        $element.hide().show(0);
      });
    }
  };
};
Sepalous answered 19/5, 2016 at 3:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.