Page scrolling using the keyboard (PgUp/PgDown, Space) sometimes gets difficult if there are elements with fixed positions at the top of the page, e.g. navigation bars: content that was not visible at the bottom of the viewport might be hidden by the fixed elements after scrolling.
How to address this problem? Do browsers calculate, how far they should scroll? I observed different behaviors for different browsers and also for the same browsers on different pages (for example, Firefox leaves about 80px of old content on http://www.sueddeutsche.de/, but far less on http://www.taz.de. Chromium leaves much more content.).
Is this a problem at all, i.e. does anybody beside me use the keyboard to scroll a web page? Do you know any statistics?
To illustrate the problem, I created a Fiddle:
https://jsfiddle.net/x7hj8c4m/
Try to scroll the content using Space on Firefox. The fixed element will cover text that was not yet visible before scrolling. If you add left: 0, it works.
Very interesting observation.
Firstly, pressing space is equivalent to pressing PgDn. And when PgDn is pressed, the page should scroll vertically by roughly "one page's worth of px". As shown by the OP's fiddle, Firefox in particular calculates this value differently, depending on whether it detects a fixed header.
From my own tests on IE, Chrome, Firefox, I deduced that:
Without a position: fixed element, Chrome and IE scroll down by ~87.5% of the document height; Firefox scrolls down by document height - scrollbar height - ~20px.
With a position: fixed; width: 100% element at the top-left of the screen, Firefox intelligently understands that the element perceptually reduces the document height, and so applies: document height - scrollbar height - fixed element height - ~20px. The condition appears to be quite specific: the element must be fixed exactly at the top-left of the document's box model with full width in order for it to work. The other browsers (Chrome, IE) don't perform such compensation, and performs the standard 87.5% scroll.
I don't know if this is relevant, but it might have something to do with support for position: sticky.
Scrolling by keyboard is a pretty basic behaviour that probably doesn't interact too much (if at all) with the DOM, so expecting it to account for fixed elements is probably too much. There seem to be browser-specific predefined increments (I have no idea if or how they can be customized), but note that the increments are usually smaller (presumably small enough) when you use the up/down arrow keys.
Related
In the following fiddle, there is significant jitter while scrolling. The jitter is noticed only in Safari on Mac, while Chrome and Firefox scrolls the page smoothly.
https://jsfiddle.net/saptarshi17/akxuLL9x/
The jitter is a result of applying parallax effect on the first paragraph. The parallax is implemented by dynamically calculating the top css property triggered by scroll events. I am noticing this on Mac OSX El Capitan, not sure about Windows.
So far, I tried:
Added a mousewheel event-listener in addition to scroll as per this accepted answer on SO.
Tried wrapping the css("top", ...) function call within requestAnimationFrame()
Changed div from relative to absolute to avoid costly reflows.
Added preserve3d property to "hardware accelerate" position calculation as per some comments somewhere on SO.
Since 2 and 3 haven't helped, I assume the core issue has much to do with Safari's scroll implementation and less about frame-rate optimizations. There is a delay Safari's predictive rendering and the subsequent handler call which offsets the position. This results in 2 renders causing the jitter. While I understand Safari's intention behind the first render which works in most cases, this seems to be wrong in cases when the developer wants to override positioning. Maybe there is a way to tell Safari to disable the predictive rendering.
EDIT:
These issues appear to have been fixed by iOS8. Consider this issue an iOS7 and earlier bug.
I've got some content (subject to CORS) that I'm serving in an iframe, that I want to be always stretched across the bottom of the browser window. I need this content to keep the same aspect ratio and fill the entire width of the browser, otherwise things will look weird. Because I don't have access to the iframe's contents I'm using -webkit-transform: scale to properly size everything properly.
I'm calculating the scaleFactor:
width = 600; // this is the original width of the iframe's contents and never changes
scaleFactor = window.innerWidth/width;
Then I set some CSS based on that scaleFactor, whenever window.innerWidth changes size:
$container.css({
'width': (width * scaleFactor) + 'px',
'height': (height * scaleFactor) + 'px',
'padding': 0
});
$iframe.css({
'-webkit-transform': 'scale('+scaleFactor+')',
'transform': 'scale('+scaleFactor+')',
'-webkit-transform-origin': '0 0',
'transform-origin': '0 0'
});
This works perfectly everyplace except iOS where it starts to break down if you zoom in too far. The iFrame starts to drift off the page and isn't near wide enough. I have no clue what's going on here.
Images of what I'm talking about: Good scaling, Bad scaling.
I've got a test page setup here that clearly demonstrates the problem on any iOS.
Anybody have any ideas?
Here's the result of my extensive investigation before I gave up.
There are two major problems involved in applying transform: scale to content inside iframes on iOS. The first was what I pointed out in the original question, that content starts to drift away from it's specified location on the page if you are using fixed position elements. It works up to a point that seems to be based on the original size of the element, the scale factor, and presumably the viewport width. In my tests a large element might scale and position perfectly when scaled at any factor greater than 0.85. A small element might be positioned perfectly so long as the scale factor is at least >3.5. It seems almost random, so I didn't bother determining what the exact point was.
I didn't try this on relatively positioned elements, but I'm assuming they function similar to fixed position elements.
There is a rather kludgy workaround for this involving using absolutely positioned elements anchored to the bottom of the page using scroll offsets and innerHeight. i.e.:
container.css('top', (document.body.scrollTop + window.innerHeight - container.height()) + 'px');
container.css('left', document.body.scrollLeft);
And updating this on every drag, transform, pinch, resize, etc. There is some weirdness involved with this method (iOS doesn't update scroll offsets until after a drag has completely stopped) but it's workable if you absolutely had to do it.
However, even though that's a possibility, when you scale content inside iframes on iOS, if you have ANY anchor tags (or other elements that need to be clicked on), the clickable area stays unscaled. If you have an image in inside an anchor tag that's 200x100 and you scale it 2x, the image will be twice as big, but the anchor will only respond to the first 200x100. Using the iOS simulator, if you double click on an image outside the clickable area Safari is even helpful enough to darken the original bounds so you know where you could have clicked. It's pretty much a deal breaker if you want to display anything other than text/images that don't require clicking or other inputs. More information here:
CSS3 Transform scaling issue on IPad Safari
"-webkit-transform: scale(2)" doesn't affect click area of Facebook Like Button (on iPad)
Until Apple fixes these long standing bugs in mobile Safari, it seems that trying to scale iframe content using webkit-transform isn't a viable option unless you are only targeting Chrome.
Edit:
Demo of scaling issues here.
Combo of iframe, transform scale, and ios is tricky.
There are bugs with transform scale. Not always in your control.
You can try zoom property instead of scale, and use scale for firefox where its not supported.
I don't have ios here so I cant check but it often works wonders. Though you may have issues with line breaks in text with zoom.
zoom solves this bug in chrome
I have a horizontally scrolling div with sections inside. I'm using Ariel Flesler's scrollTo plugin to scroll from one section to another.
Please see: http://jsfiddle.net/carienf/qZeEe/
The fiddle works perfectly in IE 9, Safari 5.1, Opera 11.5 and Chrome 14 (all the latest versions). I can click on a link to scroll to the corresponding section and scroll or mousewheel around in it.
Problem:
In Firefox 7 (and earlier), when I have scrolled to Section 2 or Section 3 and then scroll down using the scrollbar, my position is reset to the first section. This also happens when I resize the browser window. If I scroll using the mousewheel, Firefox behaves (in other words, I stay in the current section).
My question is pretty much an exact repeat of this question: Problem mixing overflow-x, FireFox, and Javascript
Only, the accepted answer (which is to allow the horizontal scrollbar to be visible) doesn't work for my specific case. Also, the guy who posted the question has removed his example. I really need that scrollbar to stay hidden and I really don't like the idea of hiding it behind a div.
Is there a way (other than setting overflow to "auto") to stop Firefox from resetting my scroll position? Or some other way to hide the scrollbar?
UPDATE: Updated Firefox to 8.0 (still a beta version) and then the behaviour is consistent with the other browsers.
I can see a couple of options.
clip off the scroll bar. Takes some measuring and you have to position absolutely.
Add a 2nd div outside that does the vertical scrolling. That breaks your code as it currently stands, but it does resolve the scrolling problem. jsfiddle.net/s2YFM
I just ran into the same exact issue. When my "modal window" pops up, I set html.noscroll { overflow: hidden }, which unfortunately causes the window to scroll-to-top.
Here's the only solution I could make work:
function RemoveScrollbar(html) {
var scrollTop = html.scrollTop;
html.addClass('noscroll');
html.scrollTop = scrollTop;
}
This is MooTools code, but super simple to convert to jQuery or other frameworks.
So a fellow StackOverflow-er wrote out some jQuery based Javascript for me a few days ago.
It works, but there's an annoying issue with it and I wondered if anyone could help out.
The JavaScript is calculating a margin for alignment purposes. The value it calculates is is not always an exact pixel, e.g sometimes its 14.4 etc. In Firefox the issue does not exist, but in other browsers such as in Safari or Chrome when the margin is being calculated (as the screen is re-sized) the far right hand side box 'jumps' and never quite stays against the edge of the container.
As I said above, in Firefox it does not 'jump', and this is the effect I was looking for.
Is there anything that could be done to stop this 'jumping' effect or is it completely related to the browsers rendering engine and out of my control? :(
This JSFiddle contains the code/demo : http://jsfiddle.net/m4rGp/1/
(Try resizing the browser width in Firefox then Safari/Chrome and you will notice the jump on the right)
Any reply's are really, really appreciated! Many thanks
This is because of the rendering engine only. There are no decimals in pixel. 1px is the smallest addressable screen element. So before changing the margin add a Math.round
$(".barModules li").css('margin-left', Math.round(dynMargin) + "px");
yet the bouncing will be there because the element is positioned in such a way that it is bound to its left (reference). When it sees the attribute margin-right:0, it tries to activate that one too ... but due to its margin-left being set and positioning based on left .... it is bouncing like that. like a debounced function call.
If your aim is to get a dynamic margin, then you have to adjust with this, else go for a fixed width page with centered elements and you don't have to worry about overflow problem
Here's an example (an alert box will pop up with the results). Run this in Firefox then Chrome and/or Safari
http://jsfiddle.net/QeaVM/
Notice how FF correctly includes the left and right margins in the calculation of outerWidth. Notice how Safari/Chrome incorrectly state that the outerWidth of #Container is equal to that of #Frame.
Also, notice that Safari/Chrome incorrectly report #Container's margin-right as a negative number when it is explicity set to 300px
Is this a bug in web-kit or jquery?
looking at the page in the inspectors for both browsers (ignoring javascript completely) you can see the same differences (so that rules out jQuery).
It does seem odd and appears to be reported bug with webkit.
https://bugs.webkit.org/show_bug.cgi?id=13343
https://bugs.webkit.org/show_bug.cgi?id=24511
The comments mention a workaround "A workaround is setting the element to display:none (or inline-block), measuring the margin then setting display back to block."