Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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;
}
like image 547
Sarah Jean Avatar asked Apr 03 '14 16:04

Sarah Jean


3 Answers

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%
like image 153
corysimmons Avatar answered Nov 16 '22 00:11

corysimmons


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);
      });
    }
  };
};
like image 20
ognus Avatar answered Nov 16 '22 00:11

ognus


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.

like image 38
Hooman Askari Avatar answered Nov 16 '22 00:11

Hooman Askari