ScrollToOptions' behavior: 'smooth' broken in Chrome and Edge v81+ - javascript

After testing in BrowserStack, I've concluded that using scrollTo() with option parameter behavior: smooth does not work in Chrome and Edge since version 81. Version 80 of both Edge and Chrome was working as expected. According to MDN, it should be working with no asterisk. (unlike Safari)
In popular answers such as this one, using behavior: smooth is the recommended way to enable smooth-scrolling in your web application.
Here's a small reproducible:
<html>
<button onclick="goToAnchor('b')">Scroll to B</button>
<div id="a" style="height: 1000px; background-color: blue;">Blue</div>
<div id="b" style="height: 1000px; background-color: red;">Red</div>
<div id="c" style="height: 1000px; background-color: green;">Green</div>
</html>
<script>
function goToAnchor(anchor) {
let rect = document.getElementById(anchor).getBoundingClientRect();
window.scrollTo({
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
behavior: 'smooth',
});
}
</script>
The expected behavior would be that the browser window smoothly interpolate the view down to the red div. It does this properly in all versions of Firefox I've tested. In all of the versions of Chrome since v81, and all versions of Edge since v81, it seems to use the behavior of behavior: auto - i.e. it jumps to the div rather than smoothly interpolating the view.
In version 80 of both Edge and Chrome, it behaves just like Firefox, meaning this bug (?) must've been introduced in version 81 - perhaps in the shared Chromium code-base?
I find it very unlikely that I am the first person to find this issue, as it has been not been working since April, and must therefore conclude I am doing something wrong. Can someone point towards the error in the code? Or is the Chrome and Edge APIs really broken? Is the behavior hidden behind a feature flag, like in Safari?

I believe I've found the culprit, and interestingly, it seems it is Firefox that is the odd one out.
In this StackOverflow thread about detecting RDP connections, the current top answer says:
You can use the following media query:
#media screen and (prefers-reduced-motion: reduce) { . . . }
The prefers-reduced-motion part is interesting. It seems in my testing that this also changes scrollTo() calls with scroll-behavior: 'smooth' to jump rather than interpolate.
I did an addendum to the question's code example to demo the feature:
<html>
<button onclick="goToAnchor('b')">Scroll to B</button>
<p class="reduced-motion">Reduced motion is enabled.</p>
<div id="a" style="height: 1000px; background-color: blue;">Blue</div>
<div id="b" style="height: 1000px; background-color: red;">Red</div>
<div id="c" style="height: 1000px; background-color: green;">Green</div>
</html>
<style>
.reduced-motion {
display: none;
}
#media (prefers-reduced-motion) {
.reduced-motion {
display: inline;
}
}
</style>
<script>
function goToAnchor(anchor) {
let rect = document.getElementById(anchor).getBoundingClientRect();
window.scrollTo({
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
behavior: 'smooth',
});
}
</script>
It will now say "Reduced motion is enabled." next to the button depending on your OS and browser configuration. In that case, the scrollTo call will simply jump rather than interpolate.
In short, the issue is that BrowserStack's remote desktop control is also enabling this flag.

I try to make a test with the MS Edge Version 87.0.664.60 and Google Chrome Version 87.0.4280.88.
On my side, the code works fine on both browsers.
Here is my test result: (Above is the MS Edge and below one is the Google Chrome browser)
You are making this test using the BrowserStack. It is can be possible that the issue is related to BrowserStack.
I suggest you try to make a test using the actual browsers. It may help you to find the cause of the issue.

Related

Prevent percentage height from increasing when the browser navigation bars are hidden in Safari [duplicate]

I have a very odd issue... in every browser and mobile version I encountered this behavior:
all the browsers have a top menu when you load the page (showing the address bar for example) which slide up when you start scrolling the page.
100vh sometimes is calculated only on the visible part of a viewport, so when the browser bar slide up 100vh increases (in terms of pixels)
all layout re-paint and re-adjust since the dimensions have changed
a bad jumpy effect for user experience
How can avoid this problem? When I first heard of viewport-height I was excited and I thought I could use it for fixed height blocks instead of using javascript, but now I think the only way to do that is in fact javascript with some resize event...
you can see the problem at: sample site
Can anyone help me with / suggest a CSS solution?
simple test code:
/* maybe i can track the issue whe it occours... */
$(function(){
var resized = -1;
$(window).resize(function(){
$('#currenth').val( $('.vhbox').eq(1).height() );
if (++resized) $('#currenth').css('background:#00c');
})
.resize();
})
*{ margin:0; padding:0; }
/*
this is the box which should keep constant the height...
min-height to allow content to be taller than viewport if too much text
*/
.vhbox{
min-height:100vh;
position:relative;
}
.vhbox .t{
display:table;
position:relative;
width:100%;
height:100vh;
}
.vhbox .c{
height:100%;
display:table-cell;
vertical-align:middle;
text-align:center;
}
<div class="vhbox" style="background-color:#c00">
<div class="t"><div class="c">
this div height should be 100% of viewport and keep this height when scrolling page
<br>
<!-- this input highlight if resize event is fired -->
<input type="text" id="currenth">
</div></div>
</div>
<div class="vhbox" style="background-color:#0c0">
<div class="t"><div class="c">
this div height should be 100% of viewport and keep this height when scrolling page
</div></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Unfortunately this is intentional…
This is a well know issue (at least in safari mobile), which is intentional, as it prevents other problems. Benjamin Poulain replied to a webkit bug:
This is completely intentional. It took quite a bit of work on our part to achieve this effect. :)
The base problem is this: the visible area changes dynamically as you scroll. If we update the CSS viewport height accordingly, we need to update the layout during the scroll. Not only that looks like shit, but doing that at 60 FPS is practically impossible in most pages (60 FPS is the baseline framerate on iOS).
It is hard to show you the “looks like shit” part, but imagine as you scroll, the contents moves and what you want on screen is continuously shifting.
Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.
From the data we had, using the larger view size was the best compromise. Most website using viewport units were looking great most of the time.
Nicolas Hoizey has researched this quite a bit: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html
No fix planned
At this point, there is not much you can do except refrain from using viewport height on mobile devices. Chrome changed to this as well in 2016:
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/BK0oHURgmJ4
https://developers.google.com/web/updates/2016/12/url-bar-resizing
You can try min-height: -webkit-fill-available; in your css instead of 100vh. It should be solved
in my app I do it like so (typescript and nested postcss, so change the code accordingly):
const appHeight = () => {
const doc = document.documentElement
doc.style.setProperty('--app-height', `${window.innerHeight}px`)
}
window.addEventListener('resize', appHeight)
appHeight()
in your css:
:root {
--app-height: 100%;
}
html,
body {
padding: 0;
margin: 0;
overflow: hidden;
width: 100vw;
height: 100vh;
#media not all and (hover:hover) {
height: var(--app-height);
}
}
it works at least on chrome mobile and ipad. What doesn't work is when you add your app to homescreen on iOS and change the orientation a few times - somehow the zoom levels mess with the innerHeight value, I might post an update if I find a solution to it.
Demo
Look at this answer: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
// First we get the viewport height and we multiple it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);
// We listen to the resize event
window.addEventListener('resize', () => {
// We execute the same script as before
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
});
body {
background-color: #333;
}
.module {
height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */
height: calc(var(--vh, 1vh) * 100);
margin: 0 auto;
max-width: 30%;
}
.module__item {
align-items: center;
display: flex;
height: 20%;
justify-content: center;
}
.module__item:nth-child(odd) {
background-color: #fff;
color: #F73859;
}
.module__item:nth-child(even) {
background-color: #F73859;
color: #F1D08A;
}
<div class="module">
<div class="module__item">20%</div>
<div class="module__item">40%</div>
<div class="module__item">60%</div>
<div class="module__item">80%</div>
<div class="module__item">100%</div>
</div>
For me such trick made a job:
height: calc(100vh - calc(100vh - 100%))
We have new viewport units lvh, svh and dvh to the rescue. This was demonstrated in the latest Google I/O 2022 video on web works.
You would probably want to stick with dvh for the browser to adapt to the mobile device's hidden tabs while scrolling. It works the similar way for width with dvw, lvw and svw units.
Here is a neat illustration from the video: https://youtu.be/Xy9ZXRRgpLk?t=982
Can I use?
This was currently working on my Chrome canary with the flag "Experimental features" enabled.
You can do this by adding following script and style
function appHeight() {
const doc = document.documentElement
doc.style.setProperty('--vh', (window.innerHeight*.01) + 'px');
}
window.addEventListener('resize', appHeight);
appHeight();
Style
.module {
height: 100vh; /* Fallback for browsers that do not support Custom Properties */
height: calc(var(--vh, 1vh) * 100);
}
Set your body position to fixed, set the height to 100%
body { position: fixed; height: 100% }
That's it, then the mobile browser will understand what you want.
Now the body will grow or shrink following the browser's view height, no matter if there is a URL bar or not, or if there are tabs (like in mobile safari) or not. The body will always get full view.
For many of the sites I build the client will ask for a 100vh banner and just as you have found, it results in a bad "jumpy" experience on mobile when you begin to scroll. This is how I solve the problem for a smooth consistent experience across all devices:
I first set my banner element CSS to height:100vh
Then I use jQuery to get the height in pixels of my banner element and apply an inline style using this height.
var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });
Doing this solves the issue on mobile devices as when the page loads, the banner element is set to 100vh using CSS and then jQuery overrides this by putting inline CSS on my banner element which stops it from resizing when a user begins to scroll.
However, on desktop if a user resizes their browser window my banner element won't resize because it now has a fixed height set in pixels due to the above jQuery. To address this I use Mobile Detect to add a 'mobile' class to the body of my document. And then I wrap the above jQuery in an if statement:
if ($('body').hasClass('mobile')) {
var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });
}
As a result, if a user is on a mobile device the class 'mobile' is present on the body of my page and the above jQuery is executed. So my banner element will only get the inline CSS applied on mobile devices meanwhile on desktop the original 100vh CSS rule remains in place.
I came up with a React component – check it out if you use React or browse the source code if you don't, so you can adapt it to your environment.
It sets the fullscreen div's height to window.innerHeight and then updates it on window resizes.
As I was looking for a solution some days, here is mine for everyone using VueJS with Vuetify (my solution uses v-app-bar, v-navigation-drawer and v-footer):
I created App.scss (used in App.vue) with the following content:
.v-application {
height: 100vh;
height: -webkit-fill-available;
}
.v-application--wrap {
min-height: 100vh !important;
min-height: -webkit-fill-available !important;
}
You can try giving position: fixed; top: 0; bottom: 0; properties to your container.
#nils explained it clearly.
What's next then?
I just went back to use relative 'classic' % (percentage) in CSS.
It's often more effort to implement something than it would be using vh, but at least, you have a pretty stable solution which works across different devices and browsers without strange UI glitches.
The the problem still remains to this date, unfortunately. And the biggest misleading it's impossible to represent the situation by using browser's devices toolbar.
I've just solved the issue like so (tested on PC, iOS and android browsers):
.your_class {
height: 100vh,
max-height: 100%, // <-- add the line
...some other props,
}
I hope it'll save your time.
The following code solved the problem (with jQuery).
var vhHeight = $("body").height();
var chromeNavbarHeight = vhHeight - window.innerHeight;
$('body').css({ height: window.innerHeight, marginTop: chromeNavbarHeight });
And the other elements use % as a unit to replace vh.
As I am new, I can't comment on other answers.
If someone is looking for an answer to make this work (and can use javascript - as it seems to be required to make this work at the moment) this approach has worked pretty well for me and it accounts for mobile orientation change as well. I use Jquery for the example code but should be doable with vanillaJS.
-First, I use a script to detect if the device is touch or hover. Bare-bones example:
if ("ontouchstart" in document.documentElement) {
document.body.classList.add('touch-device');
} else {
document.body.classList.add('hover-device');
}
This adds class to the body element according to the device type (hover or touch) that can be used later for the height script.
-Next use this code to set height of the device on load and on orientation change:
if (jQuery('body').hasClass("touch-device")) {
//Loading height on touch-device
function calcFullHeight() {
jQuery('.hero-section').css("height", $(window).height());
}
(function($) {
calcFullHeight();
jQuery(window).on('orientationchange', function() {
// 500ms timeout for getting the correct height after orientation change
setTimeout(function() {
calcFullHeight();
}, 500);
});
})(jQuery);
} else {
jQuery('.hero-section').css("height", "100vh");
}
-Timeout is set so that the device would calculate the new height correctly on orientation change. If there is no timeout, in my experience the height will not be correct. 500ms might be an overdo but has worked for me.
-100vh on hover-devices is a fallback if the browser overrides the CSS 100vh.
I just found a web app i designed has this issue with iPhones and iPads, and found an article suggesting to solve it using media queries targeted at specific Apple devices.
I don't know whether I can share the code from that article here, but the address is this: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug
Quoting the article: "just match the element height with the device height using media queries that targets the older versions of iPhone and iPad resolution."
They added just 6 media queries to adapt full height elements, and it should work as it is fully CSS implemented.
Edit pending: I'm unable to test it right now, but I will come back and report my results.
Do not use recommended approaches such as -webkit-fill-available.
I just spent all day rushing around to fix this "bug".
Add a class when your app is loaded with a browser with a "chin".
JavaScript
// Angular example but applicable for any JS solution
#HostBinding('class.browser-has-chin') browserHasChin: boolean = false;
public ngOnInit(): void {
this.browserHasChin = this._isMobileSafari();
}
private _isMobileSafari() {
return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/) ? true : false;
}
CSS
.browser-has-chin {
#media screen and (max-device-width: 767px){
// offset with padding or something
}
}
NOTES:
There are major issues with the -webkit-fill-available prop for cross-browser compatibility.
I was able to get it working in Chrome and iOS Safari to fix the chin/height calculation issue. However it broke Android Chrome and Firefox had bugs with it too.
It seems that -webkit-fill-available was rushed into webkit at some point and perhaps adopted haphazardly by Apple as a fix for chin/height calculation?
It relies on intrinsic sizing which is NOT safe to use yet.
I have created two examples below:
To showcase how height: 100vh as height can lead to scroll in mobile chrome browsers:
code : https://codesandbox.io/embed/mobile-viewport-100vh-issue-nxx8z?fontsize=14&hidenavigation=1&theme=dark
demo: https://nxx8z.csb.app/
Solution using position: fixed to resolve the issue and with purely CSS:
code : https://codesandbox.io/s/mobile-viewport-100vh-issue-fix-forked-ypx5x?file=/index.html
demo : https://ypx5x.csb.app/
The VH 100 does not work well on mobile as it does not factor in the iOS bar (or similar functionality on other platforms).
One solution that works well is to use JavaScript "window.innerHeight".
Simply assign the height of the element to this value e.g.
$('.element-name').height(window.innerHeight);
Note: It may be useful to create a function in JS, so that the height can change when the screen is resized. However, I would suggest only calling the function when the width of the screen is changed, this way the element will not jump in height when the iOS bar disappears when the user scrolls down the page.
React hooks solution with useEffect and useState
function App() {
const [vh, setVh] = useState(window.innerHeight);
useEffect(() => {
const updateVh = () => {
setVh(window.innerHeight);
};
window.addEventListener('resize', updateVh);
return () => window.removeEventListener('resize', updateVh);
}, []);
return (
<div style={{ height: vh }}>
{vh} px
</div>
);
}
Demo: https://jsfiddle.net/poooow/k570nfd9/
Because it won't be fixed, you can do something like:
# html
<body>
<div class="content">
<!-- Your stuff here -->
</div>
</body>
# css
.content {
height: 80vh;
}
For me it was the fastest and more pure solution than playing with the JavaScript which could not work on many devices and browsers.
Just use proper value of vh which fits your needs.
The following worked for me:
html { height: 100vh; }
body {
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
}
/* this is the container you want to take the visible viewport */
/* make sure this is top-level in body */
#your-app-container {
height: 100%;
}
The body will take the visible viewport height and #your-app-container with height: 100% will make that container take the visible viewport height.
Using vh on mobile devices is not going to work with 100vh, due to their design choices using the entire height of the device not including any address bars etc.
If you are looking for a layout including div heights proportionate to the true view height I use the following pure css solution:
:root {
--devHeight: 86vh; //*This value changes
}
.div{
height: calc(var(--devHeight)*0.10); //change multiplier to suit required height
}
You have two options for setting the viewport height, manually set the --devHeight to a height that works (but you will need to enter this value for each type of device you are coding for)
or
Use javascript to get the window height and then update --devheight on loading and refreshing the viewport (however this does require using javascript and is not a pure css solution)
Once you obtain your correct view height you can create multiple divs at an exact percentage of total viewport height by simply changing the multiplier in each div you assign the height to.
0.10 = 10% of view height
0.57 = 57% of view height
Hope this might help someone ;)
Here's a work around I used for my React app.
iPhone 11 Pro & iPhone Pro Max - 120px
iPhone 8 - 80px
max-height: calc(100vh - 120px);
It's a compromise but relatively simple fix
A nice read about the problem and its possible solutions can be found in this blog post: Addressing the iOS Address Bar in 100vh Layouts
The solution I ended up in my React application is utilising the react-div-100vh library described in the post above.
Brave browser on iOS behaves differently (buggy?). It changes viewport height dynamically accordingly to showing/hiding address bar. It is kind of annoying because it changes page's layout dependent on vw/vh units.
Chrome and Safari is fine.
I solved it by putting the most outer div at position: absolute and then just setting the height to 100%:
CSS:
.outer {
position: absolute;
height: 100%;
}
HTML:
<div class="outer">
<!-- content -->
</div>
It seems like CSS fix is unreliable and JS one works fine but the element is jumping when user opens the page.
I solved the issue using JS from other answers + fadeIn animation to hide jumping.
This won't fit all the use cases, but for some of them, like a button that has to be at the bottom, could be a good solution.
The people who found this answer and are struggling with an issue of wiered jumping of elements on mobile view when scrolling downward/upward whose position is fixed w.r.t the root element need to specify the property in terms of bottom rather than top and should give a field value in px. This solves the issue
change this:
element{
position: fixed;
top: 90vh;
left: 90vh;
transform: translate(-95%, -95%);
}
to this:
element{
position: fixed;
bottom: 90px; /* this is the change */
left: 90vh;
transform: translate(-95%, -95%);
}

Getting parent width from Iframe doesn't work in Chrome or Opera browsers

I found a line of code here: parent.document.body.clientWidth How can I get a parent window's height from within iFrame using jQuery? that i use to find the width of the parent page from an iframe.
It works in Edge, IE 11, Firefox but not for Chrome or Opera.
Is there a fix or perhaps a different solution to the problem?
Example
IFrame
html
<body onresize="bodyRePr()">
<script src="examplescript.js"></script>
[...]
</body>
js
function bodyRePr(){
if (parent.document.body.clientWidth > 720){
document.getElementById("example").style.width = "460px";
}
else{
document.getElementById("example").style.width = "100%";
}
}
I see this error when opening the page in Opera, not sure what it means.
requested:
html is of another page including the iframe
<body onscroll="toggleMenu()" onload="startup()" onload="getBrowserInfo()" onresize="bodyRe()">
[...]
<nav id="profileMenu">
<iframe id="profileMenuIframe"></iframe>
<div onclick="closeProfileMenu()" class="closeBtn">╳</div>
</nav>
[...]
</body>
css
#profileMenu{
z-index: 6;
position: fixed; top: 0; left: 0;
width: 500px; height: 100%;
margin-left: -100%;
background: rgb(30,30,30);
transition: margin-left 0.2s;
}
#profileMenuIframe{
width: 500px;
}
You can't test frame/parent references offline in Chrome, using file:// URL's as shown in your example.
And you can't reference your online resources either, while testing offline, because that would be cross-site scripting.
To test your parent frame references, you either need to use a browser that supports offline URL's the same way it supports online URL's, or test both parent and frames using an HTTP server.

Javascript window.scrollTo Behavior On Different Browsers

I'm facing an issue with the scrollTo function when the body has an dir=rtl attribute. here is a jsfiddle for my case.
HTML:
window.scrollTo(-200, 0);
<html>
<head>
</head>
<body dir="rtl">
<div width="100%" style="width: 3000px; height:200px; overflow: hidden">
<div style="width: 1000px; height: 100px; border: 2px solid black; display: inline-block"></div>
<div style="width: 1000px; height: 100px; border: 2px solid red; display: inline-block"></div>
</div>
<script type="text/javascript">
window.scrollTo(-200, 0);
</script>
</body>
</html>
So if I pass a positive value for the xpos parameter, it works on IE (sort of) naturally, it scrolls from the right side of the screen for an amount of 200px. but on chrome and firefox it doesn't work, I have to pass a negative value for the scrolling to work as expected.
My question, is how I can handle this case in my code, should I do browser sniffing? or is there a better way? some feature I can test if its supported?
as othree explains in his jQuery rtl scroll type plugin there are 3 main implementations for horizontal scrolling when dir is set to rtl: WebKit, Firefox/Opera and IE
the difference between these implementations is as follows:
because you can't use jQuery I've modified othree code in this plunker and it works fine in chrome, firefox and IE11
This snippet worked for me on IE and Chrome
http://jsfiddle.net/05w4tr0g/4/
var m = -1;
var pos = window.pageXOffset;
window.scrollTo(0,0);
window.scrollTo(1, 0);
if (-1 == window.pageXOffset) m = 1;
window.scrollTo(pos, 0);
window.scrollTo(m*200, 0);
Hope that helps. The idea is that that the pageXOffset is with IE and Chrome always negative if there was scrolling. The snippet will cause a little flicker because of the test scroll to x=0 and x=-1.
You could store the m value on a page load and reuse it in a wrapper function for scrollTo (or scrollBy for that matter). You could also overload the two methods and keep it all in the window context.

PDF Hides Jquery Model Window in Safari 5.1.7

Actually I want to show the modal window over pdf and its working well in IE, Chrome & Mozilla Firefox but its not working on Safari 5.1.7
So Will any 1 please help me to find some solution on this ? I am getting this issue on Windows Operating System i.e XP, Windows 8 & Windows 7.
Here is my JS Fiddle link : http://jsfiddle.net/xdrc1nou/
Below is my code as well
HTML
<div id="divHover">try me</div>
<div id="divHoverChild">hello</div>
<iframe id="ifBG" src="about:blank"></iframe>
<iframe id="ifPDF" src="http://bitcoin.org/bitcoin.pdf"></iframe>
Javascript
$().ready(function () {
var $child = $("#divHoverChild");
var $ifBG = $("#ifBG");
$("#divHover").mouseover(function () {
$child.slideDown();
$ifBG.slideDown();
})
$child.mouseout(function () {
$child.slideUp();
$ifBG.slideUp();
});
});
It will be quite difficult to find someone with OSX Lion running Safari 5.1 to have an answer for your question (that's almost 3 versions back, with Yosemite just around the corner), and a world-wide usage of less than 0.5% of the browser.
With that in mind this answer gives you some pointers, it has not been tested by me.
In general when embedded iframes don't play nicely with the overlays on a page a common solution is applying these styles to the problematic iframes:
<iframe id="ifPDF" src="http://bitcoin.org/bitcoin.pdf" style="position: relative; z-index: -1;></iframe>
See if this does it.
A less elegant solution would be to hide the problematic iframes when overlays are triggered to be shown, and therefore avoid the overlap (since the overlapping element is gone).
Just for the record in Safari 7 it works perfectly, just like in the other modern browsers you mentioned to be functioning OK.
What about using object and ember:
<object src="http://yoursite.com/the.pdf" width="700px" height="700px" style="position: relative; z-index: -1;">
<embed src="http://yoursite.com/the.pdf" style="position: relative; z-index: -1;">
</embed>
</object>
I can't test too. I am running Linux.

jQuery scrollTop() doesn't seem to work in Safari or Chrome (Windows)

I've got a simple setup to allow a "help"-style window to be loaded and scrolled to a particular point on the page. More or less the code looks like this:
var target = /* code */;
target.offsetParent().scrollTop(target.offset().top - fudgeValue);
The target of the scroll and the fudge value are determined by a couple of hints dropped on the page, and I'm having no problems with that part of this mechanism anywhere. In Firefox and IE8, the above code works exactly like I want: the scrolled box (in this case, the page body) correctly scrolls the contained stuff to the right point in the window when it's told to do so.
In Chrome and Safari, however, the call to scrollTop() apparently does nothing at all. All the numbers are OK, and the target refers to the right thing (and the offsetParent() is indeed the body element), but nothing at all happens. As far as I can tell from googling around, this is supposed to work. Is there something funny about the renderer under Safari and Chrome?
This is jQuery 1.3.2 if that matters.
Test page: http://gutfullofbeer.net/scrolltop.html
I was having this problem in Safari and Chrome (Mac) and discovered that .scrollTop would work on $("body") but not $("html, body"), FF and IE however works the other way round. A simple browser detect fixes the issue:
if($.browser.safari)
bodyelem = $("body")
else
bodyelem = $("html,body")
bodyelem.scrollTop(100)
The jQuery browser value for Chrome is Safari, so you only need to do a detect on that.
Hope this helps someone.
Yeah, there appears to be a bug in Chrome when it comes to modifying the body, trying to make it into an offsetParent. As a work-around, I suggest you simply add another div to wrap the #content div, and make that scroll:
html, body { height: 100%; padding: 0; }
html { width: 100%; background-color: #222; overflow: hidden; margin: 0; }
body
{
width: 40em; margin: 0px auto; /* narrow center column layout */
background-color: white;
position: relative; /* allow positioning children relative to this element */
}
#scrollContainer /* wraps #content, scrolls */
{
overflow: auto; /* scroll! */
position:absolute; /* make offsetParent */
top: 0; height: 100%; width: 100%; /* fill parent */
}
#header
{
position: absolute;
top: 0px; height: 50px; width: 38.5em;
background-color: white;
z-index: 1; /* sit above #content in final layout */
}
#content { padding: 5px 14px 50px 5px; }
Tested in FF 3.5.5, Chrome 3.0.195.33, IE8
Live demonstration:
$(function() {
$('#header').find('button').click(function(ev) {
var button = $(this), target = $('div.' + button.attr('class'));
var scroll = target.offsetParent().scrollTop();
target.offsetParent().scrollTop(target.offset().top + scroll - 50);
});
});
html, body { height: 100%; padding: 0; }
html { width: 100%; background-color: #222; overflow: hidden; margin: 0; }
body { width: 40em; margin: 0px auto; background-color: white; position: relative; }
#scrollContainer { overflow: auto; position:absolute; top: 0; height: 100%; width: 100%; }
#header { position: absolute; top: 0px; height: 50px; width: 38.5em; background-color: white; z-index: 1; }
#content { padding: 5px 14px 50px 5px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id='header'>
Header Box
<button class='A'>A</button>
<button class='B'>B</button>
<button class='C'>C</button>
</div>
<div id='scrollContainer'>
<div id='content'>
<div style='height: 50px'> </div>
<div class='A'>
<h1>A</h1>
<p>My name is Boffer Bings. I was born of honest parents in one of the humbler walks of life, my father being a manufacturer of dog-oil and my mother having a small studio in the shadow of the village church, where she disposed of unwelcome babes. In my boyhood I was trained to habits of industry; I not only assisted my father in procuring dogs for his vats, but was frequently employed by my mother to carry away the debris of her work in the studio. In performance of this duty I sometimes had need of all my natural intelligence for all the law officers of the vicinity were opposed to my mother's business. They were not elected on an opposition ticket, and the matter had never been made a political issue; it just happened so. My father's business of making dog-oil was, naturally, less unpopular, though the owners of missing dogs sometimes regarded him with suspicion, which was reflected, to some extent, upon me. My father had, as silent partners, all the physicians of the town, who seldom wrote a prescription which did not contain what they were pleased to designate as _Ol. can._ It is really the most valuable medicine ever discovered. But most persons are unwilling to make personal sacrifices for the afflicted, and it was evident that many of the fattest dogs in town had been forbidden to play with me--a fact which pained my young sensibilities, and at one time came near driving me to become a pirate.
</div>
<div class='B'>
<h1>B</h1>
<p>
Looking back upon those days, I cannot but regret, at times, that by indirectly bringing my beloved parents to their death I was the author of misfortunes profoundly affecting my future.
<p>
One evening while passing my father's oil factory with the body of a foundling from my mother's studio I saw a constable who seemed to be closely watching my movements. Young as I was, I had learned that a constable's acts, of whatever apparent character, are prompted by the most reprehensible motives, and I avoided him by dodging into the oilery by a side door which happened to stand ajar. I locked it at once and was alone with my dead. My father had retired for the night. The only light in the place came from the furnace, which glowed a deep, rich crimson under one of the vats, casting ruddy reflections on the walls. Within the cauldron the oil still rolled in indolent ebullition, occasionally pushing to the surface a piece of dog. Seating myself to wait for the constable to go away, I held the naked body of the foundling in my lap and tenderly stroked its short, silken hair. Ah, how beautiful it was! Even at that early age I was passionately fond of children, and as I looked upon this cherub I could almost find it in my heart to wish that the small, red wound upon its breast--the work of my dear mother--had not been mortal.
</div>
<div class='C'>
<h1>C</h1>
<p>It had been my custom to throw the babes into the river which nature had thoughtfully provided for the purpose, but that night I did not dare to leave the oilery for fear of the constable. "After all," I said to myself, "it cannot greatly matter if I put it into this cauldron. My father will never know the bones from those of a puppy, and the few deaths which may result from administering another kind of oil for the incomparable _ol. can._ are not important in a population which increases so rapidly." In short, I took the first step in crime and brought myself untold sorrow by casting the babe into the cauldron.
</div>
<div style='height: 75em;'> </div>
</div>
</div>
$("body,html,document").scrollTop($("#map_canvas").position().top);
This works for Chrome 7, IE6, IE7, IE8, IE9, FF 3.6 and Safari 5.
2012 UPDATE
This is still good but I had to use it again. Sometimes position doesn't work so this is an alternative:
$("body,html,document").scrollTop($("#map_canvas").offset().top);
The browser support status is this:
IE8, Firefox, Opera: $("html")
Chrome, Safari: $("body")
So this works:
bodyelem = $.browser.safari ? $("body") : $("html") ;
bodyelem.animate( {scrollTop: 0}, 500 );
For the scroll : 'html' or 'body' for setter (depend on browser)... 'window' for getter...
A jsFiddle for testing is here : http://jsfiddle.net/molokoloco/uCrLa/
var $window = $(window), // Set in cache, intensive use !
$document = $(document),
$body = $('body'),
scrollElement = 'html, body',
$scrollElement = $();
var isAnimated = false;
// Find scrollElement
// Inspired by http://www.zachstronaut.com/posts/2009/01/18/jquery-smooth-scroll-bugs.html
$(scrollElement).each(function(i) {
// 'html, body' for setter... window for getter...
var initScrollTop = parseInt($(this).scrollTop(), 10);
$(this).scrollTop(initScrollTop + 1);
if ($window.scrollTop() == initScrollTop + 1) {
scrollElement = this.nodeName.toLowerCase(); // html OR body
return false; // Break
}
});
$scrollElement = $(scrollElement);
// UTILITIES...
var getHash = function() {
return window.location.hash || '';
},
setHash = function(hash) {
if (hash && getHash() != hash) window.location.hash = hash;
},
getWinWidth = function() {
return $window.width();
},
// iphone ? ((window.innerWidth && window.innerWidth > 0) ? window.innerWidth : $window.width());
getWinHeight = function() {
return $window.height();
},
// iphone ? ((window.innerHeight && window.innerHeight > 0) ? window.innerHeight : $window.height());
getPageWidth = function() {
return $document.width();
},
getPageHeight = function() {
return $document.height();
},
getScrollTop = function() {
return parseInt($scrollElement.scrollTop() || $window.scrollTop(), 10);
},
setScrollTop = function(y) {
$scrollElement.stop(true, false).scrollTop(y);
},
myScrollTo = function(y, newAnchror) { // Call page scrolling to a value (like native window.scrollBy(x, y)) // Can be flooded
isAnimated = true; // kill waypoint AUTO hash
var duration = 360 + (Math.abs(y - getScrollTop()) * 0.42); // Duration depend on distance...
if (duration > 2222) duration = 0; // Instant go !! ^^
$scrollElement.stop(true, false).animate({
scrollTop: y
}, {
duration: duration,
complete: function() { // Listenner of scroll finish...
if (newAnchror) setHash(newAnchror); // If new anchor
isAnimated = false;
}
});
},
goToScreen = function(dir) { // Scroll viewport page by paginette // 1, -1 or factor
var winH = parseInt((getWinHeight() * 0.75) * dir); // 75% de la hauteur visible comme unite
myScrollTo(getScrollTop() + winH);
};
myScrollTo((getPageHeight() / 2), 'iamAMiddleAnchor');
There is a bug in Chrome (not in Safari at the time we checked) that gives unexpected results in Javascript's various width and height measurements when opening tabs in the background (bug details here) - we logged the bug in June and it's remained unresolved since.
It's possible you've encountered the bug in what you're attempting to do.
setTimeout(function() {
$("body,html,document").scrollTop( $('body').height() );
}, 100);
This probably should work even if time is 10ms.
how about
var top = $('html').scrollTop() || $('body').scrollTop();
Works for Safari, Firefox, and IE7 (haven't tried IE8). Simple test:
<button onclick='$("body,html").scrollTop(0);'> Top </button>
<button onclick='$("body,html").scrollTop(100);'> Middle </button>
<button onclick='$("body,html").scrollTop(250);'> Bottom </button>
Most examples use either one or both, but in reverse order (i.e., "html,body").
Cheers.
(And semantic purists out there, don't bust my chops -- I've been looking for this for weeks, this is a simple example, that validates XHTML strict. Feel free to create 27 layers of abstraction and event binding bloat for your OCD peace of mind. Just please give due credit, since the folks in the jQuery forums, SO, and the G couldn't cough up the goods. Peace out.)
Which element is the offsetParent of another is not well-specified and may vary across browsers. It is not guaranteed to the be the scrollable parent you are looking for.
The body itself also shouldn't be the page's main scrollable element. It only is in Quirks Mode, which in general you would want to avoid.
The offsetTop​/​offsetLeft​/​offsetParent measurements aren't terribly useful by themselves, they're only really reliable when you use them in a loop to get the total page-relative co-ordinates (as position() in jQuery does). You should know which is the element you want to scroll and find out the difference in page co-ordinates between that and the descendant target to find out how much to scroll it by.
Or if it's always the page itself you're talking about scrolling, just use a location.href= '#'+target.id navigation instead.
This appears to be working in FF and WebKit; IE not tested so far.
$(document).scrollTop();
It worked for me, just leave it to the jQuery.
$("html,body").animate({ scrollTop: 0 }, 1);
Basically you should know the browser and write the code considering browser differences. Since jQuery is cross-browser it should handle the first step. And finally you fake the js-engine of the browser by animating the scrolling in 1 millisecond.
There is not a big choice of elements that might get auto-assigned with a scrollTop value as we scroll a webpage.
So I wrote this little function to iterate through the probable elements and return the one we seek.
var grab=function (){
var el=$();
$('body#my_body, html, document').each(function(){
if ($(this).scrollTop()>0) {
el= ($(this));
return false;
}
})
return el;
}
//alert(grab().scrollTop());
In Google chrome it would get us the body, in IE - HTML.
(Note, we don't need to set overflow:auto explicitly on our html or body that way.)
I was facing this problem, I created this link at the bottom and implemented the jQuery scrollTop code and it worked perfectly in Firefox, IE, Opera but didn't work in Chrome and Safari. I'm learning jQuery so I don't know if this solution is technically perfect but this worked for me. I just implemented 2 ScrollTop codes the first one uses $('html') which works for Firefox, etc. The second one uses $('html body') this works for Chrome and Safari.
$('a#top').click(function() {
$('html').animate({scrollTop: 0}, 'slow');
return false;
$('html body').animate({scrollTop: 0}, 'slow');
return false;
});
Indeed, seems like animation is required to make it work in Safari. I ended up with:
if($.browser.safari)
bodyelem = $("body");
else
bodyelem = $("html,body");
bodyelem.animate({scrollTop:0},{queue:false, duration:100, easing:"linear", complete:callbackFunc});
I am not sure if this is the case:
I was using Google's CDN for jQuery i.e.
Putting "https:" before //ajax.google.......
worked, it seems Safari recognized it as a local path (checked it by - Inspect Element)
Sorry, only tested in Safari 7.0.3 :(
I my case, the button was working for two of 8 links. My solution was
$("body,html,document").animate({scrollTop:$("#myLocation").offset().top},2500);
This created a nice scroll effect as well
To summarise solutions from a couple of questions/answers:
If you want to get the current scroll offset use:
$(document).scrollTop()
To set the scroll offset use:
$('html,body').scrollTop(x)
To animate the scroll use use:
$('html,body').animate({scrollTop: x});
It's not really a bug, just a difference in implantation by the browser vendors.
As a rule avoid browser sniffing. There is a nifty jQuery fix which is hinted at in the answers.
This is what works for me: $('html:not(:animated),body:not(:animated)').scrollTop()

Categories

Resources