Absolute Positioned Floating Header Jitters in Safari - javascript

I have a simple JSFiddle of a single floating header here:
http://jsfiddle.net/zT9KQ/
Basically, this uses translate3d to kick in the GPU and hardware accelerate the floating header so that it may be drawn more smoothly. The header jitters in the latest Safari but gets drawn perfectly fine in the latest Chrome, FF and Opera. The actual code that is being affected by this (the code that spurred this question) is code I've written that cannot be shared publicly but works in a similar manner where fixed positioning is, unfortunately, not a valid solution.
I have tried:
Setting the backface-visibility CSS property to none.
Setting the perspective CSS property to 1000.
Playing with requestAnimationFrame during the animation logic.
Throttling the scroll event callback.
Setting the translateZ transform to something higher than 0px.
But none of this has worked (or at least - it seems reasonable to assume the GPU has kicked in but the jittering persists). I noticed that two questions are already open that are identical this one I'm raising but no one has answered them:
Jitter in Sticky Header in Safari
Implementing fixed position in javascript causes jitter in Safari when scrolling
Is this a known bug? Is there a performance hole I'm not sealing up?
EDIT
I have been receiving a lot of questions as to why position: fixed is not a valid option. To directly reply to Antony's comment on the question itself:
I'm not emulating/reinventing position: fixed. If you look at the top-voted answer (as of this comment), you will see that this seems to be a Safari issue. The reason position: fixed is undesirable in this case is because the code in question must be able to support multiple floating headers that sit below each other and have a "container" range where there may be infinitely nested containers. Using fixed positioning not only makes the code more complicated in the case where these floating headers live in a container that horizontally scrolls but also makes the component more brittle overall (calculating offsets when the widget needs to sit within another container somewhere else on the page). So, semantically, absolute positioning fits my needs better than fixed.
SECOND EDIT
Upon thinking about what Antony had been telling me (that I may be reinventing the wheel), and after hearing about -wekbit-sticky from user3716477, I would like to update the question to show what I'm trying to do. You can see how my code behaves in every browser other than Safari here:
http://cl.ly/3y1i3C473G2G
I have learned:
You cannot rely on the scroll or really any scroll-like events (such as mousewheel) since they are asynchronous in nature. I submitted a bug to Apple detailing what was happening and they closed the bug for this reason.
There is no real way to do what I want as of now - to have multiple floating headers that stack and replace each other. I guess I'll have to wait for something like -webkit-sticky to come out.
I should include all relevant information in SO questions from here on out. :-P
Thanks for playing guys! Here's the exact response I received from Apple:
Apple Developer Relations09-Jun-2014 01:16 PM
Engineering has determined that there are no plans to address this
based on the following:
Code is using scroll events, which are asynchronous.
We are now closing this bug report.
If you have questions regarding the resolution of this issue, please
update your bug report with that information.
Please be sure to regularly check new Apple releases for any updates
that might affect this issue.

Since there is an apparent delay between scrolling with the trackpad and the scroll event firing, you can attach the handler to an additional mousewheel event to smoothen things up.
$scrollContainer.on('scroll mousewheel', function () {
// reinvent the wheel here
});
You can see in this demo here that jittering is far less likely to occur when you scroll with the trackpad. In the demo, I have invoked the handler on load to eliminate the flash when you first scroll on Safari. There may still be some occasional jitter, but if you want to minimize that, you can go the resource intensive way of using setInterval and requestAnimationFrame.
This might fix the problem for now, but as I have said before, this emulation approach is not ideal and you are very likely to run into more trouble down the road.

It appears to be a bug with Safari scrolling. If you drag the scrollbar manually (don't use the trackpad gesture to scroll), then there is no jittering.
Chrome (and other browsers) handle scrolling differently which is why this bug is only present on Safari. You might want to submit a bug report to Apple.

How about a little restructuring, like this:
http://jsfiddle.net/me2loveit2/zT9KQ/6/
html:
<div>
<h1>Header</h1>
<div class="container">
<div class="content"></div>
</div>
</div>
CSS:
.container {
height: 300px;
position: relative;
overflow-y:scroll;
}
.content {
height:1000px;
}
h1 {
position: relative;
top:0px;
left:0px;
margin: 0;
width: 100%;
background: black;
color: white;
}

If you are using something similar to the jiddle code you have to check if browser is safari and then make it a different way like:
$(function () {
var $header = $('h1'),
$container = $('.container'),
$scrollContainer = $('.scroll-container'),
scrollContainerOffset = $scrollContainer.offset().top;
$scrollContainer.on('scroll', function () {
var top = Math.max(0, $container.offset().top * -1 + scrollContainerOffset);
$header.css('top', top + 'px');
});
});
Even when it isn't the way you want your problem to be solved, it might be a workaround that may help...

Scroll events are sent asynchronously in various browsers; you should not rely on them to do things like this.
The best solution would be to use position:-webkit-sticky; top: 0;

You could try and disable all and any javascript-ish scroll handling and just simply remove
position: relative; from the .container.
Afterwards just add h1{ top:0; } and it will happily stick to the .scroll-container.
Excerpt from CSS absolute positioning:
Position it at a specified position relative to its closest positioned ancestor...
To explain - your absolute H1 will look up the tree for the first ancestor element which defines a "position" property and inherit it as a 0,0 reference point.
It could be a WTF at first, but this behaviour is a powerhouse once you tame it.
EDIT: Related to the original jsFiddle, I made some more property removals:
http://jsfiddle.net/253Ss/
^ .container wrapper could be removed as well, since it is no more neccessary in a technical sense.

Related

Asynchronous scrolling (APZ) in Edge janky & problematic; not in IE or other browsers

The Edge browser causes janking of certain divs when they are adjusted by a scroll listener. This janking does not occur in IE, Chrome, Opera, or Firefox.
Even though Firefox doesn't cause janking, it raises a flag in the console when it detects the scroll listener is first employed, and it directs me to this page for explication:
https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects
The article describes what seems to be an exact desciption of the issue I'm having with Edge:
In the asynchronous scrolling model, the visual scroll position is
updated in the compositor thread and is visible to the user before the
scroll event is updated in the DOM and fired on the main thread. This
means that the effects implemented will lag a little bit behind what
the user sees the scroll position to be. This can cause the effect to
be laggy, janky, or jittery — in short, something we want to avoid.
It only offers two solutions, the first of which (position: sticky) only has limited browser support, and the second of which (scroll snapping) has been removed from web standards.
Reaching further, I discovered a good article on APZ (Asynchronous Panning & Zooming) in Edge by its development team:
https://blogs.windows.com/msedgedev/2017/03/08/scrolling-on-the-web/#ahrEuFu6fybJ1uwj.97
It would be ideal if there were a way to turn APZ on and off, but I can't find a way around it. However Firefox has implemented it poses no problem for my routine, but Edge's implementation is extremely problematic, especially since it provides no support for position: sticky.
Is there another way around this issue? It is a problem.
You can force edge to turn off it's APZ implementation by wrapping the scrollable element inside another, forcing that wrapper element into it's own layer, and then finally adding in another dummy, position fixed element, inside that wrapper.
Your HTML would be like so:
// Wrapper element, forced into it's own layer with translateZ.
<div id="wrapper_element" style="transform: translateZ(0);">
// Dummy position fixed element to force APZ off.
<div style="top: 0px; width: 1px; height: 1px; position: fixed; z-index: 1;"></div>
// Your current scrollable element.
<div id="your_scrollable_element">
// Your content
</div>
</div>
This should fix your problem.

Can I do anything about "repaints on scroll" warning in Chrome for "overflow:scroll" div

In Chrome DevTools, under Rendering, there's an option to "Show potential scroll bottlenecks".
When I enabled this, some div elements I have on the screen with overflow:scroll show a flag at the top saying "repaints on scroll."
I can't find a lot of documentation on this feature, and I don't know whether it's something I can actually fix or improve upon, or just a statement of fact - the divs have content, and they do indeed scroll.
You can apply this CSS on the div with overflow: scroll or overflow: auto that create scroll bottlenecks.
transform: translateZ(0);
-webkit-transform: translateZ(0);
That will force the browser to create a new layer to paint this element, and sometimes fix scroll bottlenecks (especially with Webkit).
Although the accepted answer solves the problem, it is worth looking at the CSS will-change property. This is preferred over transform: translateZ(0); in recent times. Here is an that article explains the difference in detail - https://dev.opera.com/articles/css-will-change-property/
.scroll-container {
will-change: transform;
}
This amazingly took me multiple days to track down what was going on, and only because I saw the one side-comment at the end of a bug report at
Chromium bugtracker Issue 514303. Here's what's going on and how to fix it:
There exists a concept called "LCD text", which I believe means subpixel antialiasing, i.e. "crisper sharper text". Unfortunately, this feature is mutually incompatible with compositor-accelerated scrolling.
LCD text is enabled by default (at least on Blink/Webkit?) on all platforms which are not high-DPI (most normal monitors; i.e. you can check console.log(devicePixelRatio)). On the other hand, LCD text is DISABLED by default on high-DPI devices (think Retina display, or most mobile devices and tablets) since you don't really need a "crisper sharper text" feature on high-DPI platforms.
Therefore the opposite is true for compositor-accelerated scrolling: it is only possible on high-DPI platform where LCD text is disabled.
However, you can force compositor-accelerated scrolling on most monitors by promoting the overflow:scroll element to its own layer, by either adding will-change:transform to that element, or any hackish equivalent which will force the overflow element to be the parent of its own layer (such as transform:translateZ(0)). (Do note that vendor prefixes are being removed.)
tl;dr: Chrome doesn't suppose both subpixel antialiasing AND gpu-assisted scrolling; pick one or the other. Subpixel antialiasing is the default pick on Chrome (except on cellphones and retina displays, because their text is so small you don't need the feature, so you won't notice this issue on those platforms). Override this by forcing the element into its own compositor Layer with will-change:transform (but note that maybe your text won't look crystal perfect).
Nope, you cant modify that, it is a Chrome function to allow you to know, what's painted each update in the window.
Updates can be a lot of different things (scroll, mousemove, interval, requestanimationframe,...).
But, now you know that, you can enhance your code.
If (I dont know), the browser alway re-paint a div if it is set to overflow scroll you maybe can do some JS to set to overflow hidden when out of screen...
This post talk about different Browser layout

Apple store style fixed div on LEFT

I'm pulling my hair out over this. I have a webpage where I'd like to have a fixed position div on the left (the parrot & translater) follow the page as it scrolls down.
http://www.cartoonizemypet.com/new/help/
I managed to follow this tut http://jqueryfordesigners.com/fixed-floating-elements/ and get what I thought was a perfect effect! Then I tried viewing it on my phone.... As soon as I zoomed in the blasted div moved over the text! :( You can see the affect on a regular browser by shrinking the browser window and scrolling to the right.
Does anyone know a way to prevent the parrot from moving horizontally? I've been searching high and low for a solution but it's starting to seem impossible.
Here's the relevant CSS
#content {
padding-top:20px;
padding-bottom:713px; /* Height of the footer element */
width:888px;
margin-left:auto;
margin-right:auto;
position:relative;
}
#help-col1 {
left:0;
width:218px;
position:absolute;
height:500px;
}
#parrot-box {
position:absolute;
top:0;
margin-top: 20px;
}
#parrot-box.fixed {
position:fixed;
top:0;
}
#help-col2 {
width:634px;
float:right;
}
Feel free to check out the page source (http://www.cartoonizemypet.com/new/help/) to see the SCRIPT and HTML. Any help would be MUCH appreciated.
When the parrot gets the 'fixed' class, The parrot (inside #help-col1) has a 'left' value of 0. This means he's always going to be attached to the left side of the page... no matter what the dimensions of the window are, and how it scrolls.
What you're asking for is for him to behave like a fixed positioned element when the user scrolls vertically, but not horizontally. As far as I know, this isn't possible. Fixed is fixed... x and y.
However, there are some solutions (like this one) that talk about using javascript to get over this problem. The theory here is that a little javascript can listen to when the page has been horizontally scrolled and if it has, nudge the parrot back into place accordingly.
Personally, I'd look into using css media queries to make a mobile specific layout. You can assign specific CSS for the mobile version of the site, so hopefully the user doesn't need to zoom (or horizontally scroll) at all =)
Good luck!
JS scroll event listener has been suggested, but all implementations relying on it are systematically laggy. I reckon you would have better luck using media queries to determine whether or not fixed positioning is appropriate (i.e. OK if the window/device is wide enough, or substitute with an alternative behaviour if not).
You could actually leave the parrot at the top for narrow screens and preserve some real estate as well as address older mobile Safari versions' inability to correctly interpret position:fixed. You could certainly implement out a more refined approach, but this should be a good starting point - to try it out, execute the following script on your page (just in the console is fine):
$('head').append('<style type="text/css">#parrot-box.fixed {position:absolute;}</style><style type="text/css" media="screen and (min-width: 982px)">#parrot-box.fixed {position:fixed !important;}</style>');
First it overrides the original #parrot-box.fixed declaration, and then applies your floated styling to whenever the window is at least 982px wide (your page wrapper width).
Not to worry everyone, my husband figured out an alternative way of making this work! :)
Rather than moving the parrot with the page; I'm going to have multiple versions of the parrot inside the answer divs. That way when a user clicks on an answer it pops open and the parrot appears beside it.
Not how I originally had it planned out, but I think I can make this new way look even better!
Thanks for the help at any rate! :)

Rendering bug in WebKit browsers

In the project I currently work on we experience very strange rendering issue. The worst thing is that this issue emerges completely spontaneously and after several days of testing we haven't managed to find the sequence of actions wich would reproduce this issue. Here is an explanation of how this bug look like. Here is a screenshot of how the page should look like:
But instead of this after some manipulations content block pops up so only the part of the content is visible and its look like:
The most strange thing is that such a position of the block is not based on values of CSS properties as shown by Web Inspector.
As you can see the CSS properties are ok, while the position of the block is not. This fact suggest me that it could be some rendering bug of the WebKit engine
The project is built using Ext JS 3.4 and it is a classical one-page web application. This issue was seen in the last versions of Chrome and Safari on Mac OS 10.7/10.8. Though due to the spontaneous nature of this issue it might be present in other browsers and platforms too.
Any piece of advice on how to debug such issues or how it could arise is welcome.
Please check if any of your code or Ext JS's code is using scrollIntoView method, we have seen similar issue when scrollIntoView is called on any element that does not have overflow set to auto and it is inside an clipped element that is probably placed relatively positioned.
It seems bug in webkit because it scrolls clipped element which is not happening in other browsers.
I also see two elements in same hierarchy which has overflow set to auto. And scrollIntoView is scrolling wrong element.
Chrome and safari on Mac are having problems with scrolling. If the element has been scrolled and the content changes, the scroll position is kept even if the content is not high enough to require a scrolling.
The work around we have found in our application is to resize the container (the one that has the scroll) so that it has the scrollbar (or else you cannot play with the scrolling properties) and then reset the scrolling, and the height.
$(container).css('height',1).scrollTop('1').css('height','');
Here is how we do it in jQuery. You will not even see a flickering :)
I am not sure if it is the problem, but this thing kept us on our feet for a while.
i went through the same problem while working with a sencha touch 2 app and because thats same as ExtJS i have a solution for you
this probably is a bug in the framework and this happens when the ExtJS renders the application before the browser populates mayb the correct window.innerWidth and window.innerHeight and thus the viewport cannot take the correct width and height. this also explains the randomness of the event. This becomes more prominent when used on mobiles probably because of the limited resources and slow response.
the solution that i took to handle this mayb isnt a good one but i couldnt find a better one considering is a glitch in the framework itself
i poll for the correct height and width of the browser for around a sec after every say 100ms for the correct height and width of the window and if i find that the height OR width of the viewport isnt same i re adjust it. because you are working with ExtJS and app would run on high powered systems(as compared to mobile phones) i would recommend a smaller interval and then to be safe a larger time period to which it polls.
heres the code that i use currently edit according to your needs
var aId = setInterval(function () {
if (Ext.Viewport.getWidth() !== window.innerWidth || Ext.Viewport.getHeight() !== window.innerHeight) {
Ext.Viewport.setSize(window.innerWidth, window.innerHeight);
clearInterval(aId);
}
num = num + 1;
if (num > 10) {
clearInterval(aId);
}
}, 100)
i currently use this code inside the launch function of the app. but you can also use this inside the show event of the viewport for which you should keep the interval time to minimum possible to avoid any lags.
with this if you think this app might be used on devices where the window height and width would be changed by the user (like that of mobile browser when the orientation changes or if you think user would change the height and width of the browser window). then just copy & paste the same code piece inside the viewports resize event so that it also polls and resizes viewport when the size of the viewport changes.
Did you try adding a clear:both; block after the toolbar div ?
<div style="clear:both;"></div>
#bjornd it's pretty hard to debug without any code :)
Is the toolbar positioned and has the content an ID that's called in the URL?
In other words: is there some link (e.g.) that triggers #content and has no preventDefault() etc? This would scroll the page probably.
I dunno, this was the first thing that came to mind.
It could also be the toolbar content that is (for some reason) no longer cleared or some change in the content's top position (relative to another changed/removed element?)
Try and create a stripped-down test-case that contains the simplest of code but still triggers the bug. If you post that (through e.g. a Fiddle etc) we can have a proper look.
It might be a css issue;
I've had a similar issue using equal height divs by setting a padding-bottom: 99999px; and margin-bottom: -99999px;. Which workes fine in all cases, except when you use hashtag anchors to jump to a div further on the page. Jump down.
In that case the top of the page clipped and started with the div I wanted to see.
Since you say the problem is pretty hard to track, this might be something to have a look at. The solution was to remove these 2 css lines and use another method of setting div heights.

vertical scroll bar not working when using window xp theme (not classic)

This may sound crazy, and i didnt believe it until i saw it for myself.
The vertical scroll bar does not scroll when you click in the space between the scroller or the arrows. You have to drag the bar to get it to scroll. This only happens in the ugly default theme (not windows classic).
The scroll bar has some heavy javascript behind it that drive scrolling of another DIV on the page.
Has anyone even encountered this before? Why the heck does the theme influence IE's rendering?! Only IE!
edit:
this problem happens when you are in "non-classic" XP theme (the default ugly one). switching themes does not cause this, but it does fix it if you switch to classic from the default.
Does it help when you add
<!--[if IE]><meta http-equiv="MSThemeCompatible" content="no"><![endif]-->
to your page source? It worked well to work around glitches in IE when a non-classic theme is enabled for me. Note that the conditional comments are there so that firefox does not parse the tag (because it will screw up scrollbars in firefox sometimes).
I just had the same problem with vertical scrollbar in IE7 within the XP theme. After many experiments, I finally found the solution to it. I don't know this solution fits your case.
The container (div#scroll in the example below) with the rendered scrollbars must be larger than 18px. Any number below that will cause the vertical bar disabled.
<div id="scroll">
<div id="fill">
</div>
</div>
The stylesheet, for example:
#fill{
width: 1px;
height: 1000px;
}
#scroll {
width: 18px; /* This has to be larger than or equal to 18. */
height: 50px;
overflow-y: scroll;
}
Thanks,
Grace
In order to avoid certain Windows restrictions, IE re-implements almost all the controls visible on a web page.
The Old New Thing : Windowless controls are not magic
<excerpt>
The Internet Explorer team went and reimplemented all of the controls that a web page would need. They have their own windowless checkbox control, a windowless listbox control,
a windowless edit box, and so on. In addition to reproducing all the functionality of the windowed controls, the Internet Explorer folks also had to reproduce the "look" of the
windowed controls, down to the last pixel. (Functions like
DrawThemeBackground and DrawFrameControl prove extremely helpful here.)
If I recall correctly, the only element that is still windowed is the <SELECT> element.
If you squint, you can see some places where they didn't quite nail it. For example, if you right-click in a text box, options like "Right to left reading order" and "Insert Unicode control character" are missing. As another example, notice that IE's scroll bars do not light up when you hover over them.
</excerpt>
Without seeing the code implementing your page, I would guess that this is another manifestation of "IE's controls don't act native".
If you have a support contract with Microsoft, I'd suggest complaining to them through that channel; if not, see if you get any responses in the microsoft.public.internetexplorer.general newsgroup. Back in IE6, they broke the scrollbar with KB824145 and fixed it with KB832894, so it's not like theses sorts of problems haven't happened and been resolved before.
First, I'll start saying that IE is a terrible browser and in some cases you don't have control over issues while developing a web application.
In that case I haven't encounter such an issue and a Google search don't pull anything on the subject. I'd say it must be something in your code that lead to a weird bug. Most likely it won't break in a lot of systems, so unless it's a very important application (sales, government...) you could leave it that way.
Look at it that way: who change the style of windows to classic? Geeks. And do geeks use IE? No. ^^ ... Joking, but not really.
If it's important I'll need to take a look at your actual code for "The scroll bar has some heavy javascript behind it that drive scrolling of another DIV on the page."

Categories

Resources