It seems body.scrollTop (and body.scrollLeft) are deprecated in ES5 strict-mode. What is the reason for this, given that it still seems okay to use these properties on other DOMElements?
Background Info:
I have a function that tries to increase (or decrease, as specified) the scrollTop values of all the ancestors of an element, till one of these actually changes. I am wondering if, to stay complaint with strict-mode, I should specifically check against the body element as the chain of parents moves upward.
[Obviously, bodyrefers to document.body]
It's Chrome's own incorrect behavior that is deprecated, and they're warning authors to stop relying on it.
The scrolling viewport is represented by document.documentElement (<html>) in standards mode or <body> in quirks mode. (Quirks mode emulates the document rendering of Navigator 4 and Explorer 5.)
Chrome uses body.scrollTop to represent the viewport's scroll position in both modes, which is wrong. It sounds like they want to fix this so they're encouraging authors to script for the standard behavior.
I don't think you need to change your code. There's nothing wrong with using body.scrollTop in standards mode so long as you understand it represents the scroll position of body only (typically 0, unless you've given body a scroll box).
You can see the warning by executing document.body.scrollTop in the console:
body.scrollTop is deprecated in strict mode. Please use documentElement.scrollTop if in strict mode and body.scrollTop only if in quirks mode.
I noticed my code stop working on newer versions of Chrome. I fixed it by using window.scrollY
Before:
var scrollTop = document.body.scrollTop;
Now:
var scrollTop = window.scrollY;
It works all the time now. You can find more documentation here.
Also, I was using:
document.body.scrollTop = 0;
now I replaced it with:
window.scrollTo(0, 0);
The following code works for me to set the popup in a correct position whenever the click event fires, for all browsers.
var scrollTop = window.scrollY; //For all browsers.
var scrollTop = document.body.scrollTop; //This works for only IE Edge specific versions
scrollTop refers to how much the element is scrolled. This means body shouldn't have a scrollTop because it is never scrolled, body has the topmost scrollbar so it's contents can be scrolled but not body itself.
The last picture on this page explains a lot:
https://developer.mozilla.org/en-US/docs/Web/API/Element.scrollTop
Related
This may come as a huge surprise to some people but I am having an issue with the IE browser when I am using the $(window).scroll method.
My goal:
I would like to have the menu located on the left retain it's position until the scroll reaches > y value. It will then fix itself to the top of the page until the scroll returns to a < y value.
My error:
Everything seems just fine in Chrome and Firefox but when I go to Internet Explorer it would seem the browser is moving #scroller every time the scroll value changes, this is causing a moving/flickering event.
If someone could point me to a resource or give me a workaround for this I would be very grateful!
Here is a fiddle:
http://jsfiddle.net/CampbeII/nLK7j/
Here is a link to the site in dev:
http://squ4reone.com/domains/ottawakaraoke/Squ4reone/responsive/index.php
My script:
$(window).scroll(function () {
var navigation = $(window).scrollTop();
if (navigation > 400) {
$('#scroller').css('top',navigation - 220);
} else {
$('#scroller').css('top',183);
$('#scroller').css('position','relative');
}
});
You might want to take a look at the jQuery Waypoints plugin, it lets you do sticky elements like this and a lot more.
If you want to stick with your current method, like the other answers have indicated you should toggle fixed positioning instead of updating the .top attribute in every scroll event. However, I would also introduce a flag to track whether or not it is currently stuck, this way you are only updating the position and top attributes when it actually make the transition instead of every scroll event. Interacting with the DOM is computationally expensive, this will take a lot of load off of the layout engine and should make things even smoother.
http://jsfiddle.net/WYNcj/6/
$(function () {
var stuck = false,
stickAt = $('#scroller').offset().top;
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (!stuck && scrollTop > stickAt) {
$('#scroller').css('top', 0);
$('#scroller').css('position','fixed');
stuck = true;
} else if (stuck && scrollTop < stickAt) {
$('#scroller').css('top', stickAt);
$('#scroller').css('position','absolute');
stuck = false;
}
});
});
Update
Switching the #scroller from relative to fixed removes it from the normal flow of the page, this can have unintended consequences for the layout as it re-flows without the missing block. If you change #scroller to use an absolute position it will be removed from the normal flow and will no longer cause these side-effects. I've updated the above example and the linked jsfiddle to reflect the changes to the JS/CSS.
I also changed the way that stickAt is calculated as well, it uses .offset() to find the exact position of the top of #scoller instead of relying on the CSS top value.
Instead of setting the top distance at each scroll event, please consider only switching between a fixed position and an absolute or relative position.All browsers will appreciate and Especially IE.
So you still listen to scroll but you now keep a state flag out of the scroll handler and simply evaluate if it has to switch between display types.
That is so much more optimized and IE likes it.
I can get flickers in Chrome as well if I scroll very quickly. Instead of updating the top position on scroll, instead used the fixed position for your element once the page has scrolled below the threshold. Take a look at the updated fiddle: http://jsfiddle.net/nLK7j/2/
I have a page that I need to dynamically load ajax content when the user scrolls to the bottom. The problem is that JQuery is not returning the correct window height. I have used this function before and have never seen it fail, but for some reason it will return the same value as the document height. I have the test page here: bangstyle.com/test-images
I have coded the alert to display at page load, and also whenever the user scrolls 500px below the top:
function scroller() {
if($(window).scrollTop() > 500){
delay(function(){ //200ms wait
pagecounter++;
sideshow();
alert("window height: " + $(window).height() + " scrolltop: " + $(window).scrollTop() + " document height: " + $(document).height());
return false;
}, 200 );
}
}
I tried posting this before but I deleted it as I didn't get a solution. I hope it is ok to post a link to my test page. BTW I have tested this on Mac Safari and Mac FF. I have run this same code on other pages and it works fine. I feel there must be something in the dom of this page that causes JS to fail, but no idea what that would be.
Look at your HTML souce code.
The first line should be <!DOCTYPE html> and you have <style> tag instead.
So it seems that your document is running in Quirks Mode and jQuery can't calculate correct window dimensions.
//works in chrome
$(window).bind('scroll', function(ev){
//get the viewport height. i.e. this is the viewable browser window height
var clientHeight = document.body.clientHeight,
//height of the window/document. $(window).height() and $(document).height() also return this value.
windowHeight = $(this).outerHeight(),
//current top position of the window scroll. Seems this *only* works when bound inside of a scoll event.
scrollY = $(this).scrollTop();
if( windowHeight - clientHeight === scrollY ){
console.log('bottom');
}
});
I had the same problem.
I've found some things:
1) the problem happens when you try to get the actual height before document is completed rendered;
2) the problem happens in google chrome when you does not use corret DOCTYPE (mentioned above)
3) it always happens in google chrome even after the document is rendered completly.
For google chrome, I've found a workaround here: get-document-height-cross-browser
I'm using this solution only for google chrome and it resolved my problem, I expect helps someone that still have the problem.
This is an old question but I recently struggled with not getting the correct window height in IE10 by a few pixels.
I discovered that IE10 applies a 75% zoom by default and that screws the window and document measurements.
So, if you're getting wrong width or height, make sure zoom is set to 100%.
Did some looking around and stumbled upon this, don't know if it helps but it's worth bringing up.
why is $(window).height() so wrong?
Since jquery (and dom in general) is not calculating sizes correctly in quirksmode, two solutions:
Add doctype html at the top of your page (like mentioned in "correct" answer), or
Use window.innerHeight, window.innerWidth if first option is not an option.
Hope it helps.
I moved my scripts from to footer and that resolved it for me.
Has anyone tried using
$(“html, body”).animate({scrollTop:0}, 'slow');
on Opera browser?
It does a weird effect especially if you scroll on a long page, it seems like the page go first to the top and then it scroll down to the right point. It is a weird disturbing effect...
Is there any workaround to fix it? thanks
The attribute was not defined properly in the past. It was introduced by IE, I think, then reverse engineered to be implemented by different user agents. It has been since described in CSSOM (still a working draft). As of today, there is still a bug indeed in Opera which is being in the process to be fixed.
## Possible hack.
A solution will be
$(window.opera?'html':'html, body').animate({
scrollTop:0}, 'slow'
);
Be careful because if Opera fixes it at a point, the code is likely to behave strangely.
Why?
In Firefox and IE quirks mode, you have to set the property on the "body" to make the page scroll, while it is ignored if you set it on the "html".
In Firefox and IE standards mode, you have to set the property on the "html" to make the page scroll, while it is ignored if you set it on the "body".
In Safari and Chrome, in either mode, you have to set the property on the "body" to make the page scroll, while it is ignored if you set it on the "html".
Since the page is in standards mode, they have to set it on both the "html" and "body, or it won't work in Safari/Chrome.
Now here's the bad news; in Opera, when you read the scrollTop of the body it is correctly 0, since the body is not scrollable within the document. But Opera will scroll the viewport if you set the scrolling offset on either the "html" or "body". As a result, the animation sets two properties, once for the "html" and once for the "body". The first starts at the right place, while the second starts at 0, causing the flicker and odd scroll position.
Better solution not involving user agent sniffing
From http://w3fools.com/js/script.js
// find out what the hell to scroll ( html or body )
// its like we can already tell - spooky
if ( $docEl.scrollTop() ) {
$scrollable = $docEl;
} else {
var bodyST = $body.scrollTop();
// if scrolling the body doesn't do anything
if ( $body.scrollTop( bodyST + 1 ).scrollTop() == bodyST) {
$scrollable = $docEl;
} else {
// we actually scrolled, so, er, undo it
$body.scrollTop( bodyST - 1 );
}
}
This http://www.zachstronaut.com/posts/2009/01/18/jquery-smooth-scroll-bugs.html#opera might be a better solution without using any Opera specific functions and accounting for quirks mode.
$("body:not(:animated)").animate({ scrollTop: destination}, 500 );
return false;
works for me, in opera as well.
EDIT: does not work in firefox though
I've had this problem, too. This is what I use and it works. It's not browser sniffing, but it's not particularly nice code, to me. It works tho, for now.
Calling scrollTop on html element in Opera 11 / IE 8 / FF 3.6 returns a number larger than zero
Calling scrollTop on html element in Chrome 10 / Flock 3.5 / Safari 5 (for Windows) returns 0
So just test that:
If the browser is Opera, you test for a number larger than 0 on scrollTop, and call scrollTop on only html, a la
var html = document.getElementsByTagName('html')[0];
var body = document.getElementsByTagName('body')[0];
$(html).animate({scrollTop:0+'px'},{'duration':1000,'easing':'swing'});
If the browser is Chrome, or Flock or Safari than scrollTop will return 0 and you test for that, acting accordingly:
$(html,body).animate({scrollTop:0+'px'},{'duration':1000,'easing':'swing'});
So, you'll set the effect on html for standards mode FF and IE (quirks should be covered, too. Ugh), and body for Chrome and Safari.
In Opera, which attempts to scroll both html and body, thus leading to mass freakishness, scrollTop returns a number greater than 0, so you call only html and you don't get the flickery nonsense.
So you can safely use both html and body when necessary, or just html when the browser is Opera.
And don't forget yer preventDefault() or that'll be another weird flicker you'll have to worry about. ;)
Hey, I'm no JS ninja, but I try hard and this works for me, and I don't have time right now to investigate it as much as I'd like, so I thought I'd post this here and help. If I'm wrong I'll hold up my hands and say it. ;) So feedback, please. :D
Tom.
Yep!
I had a problem with action of animate({scrollTop: }) function called in click() function.
The best way is http://www.zachstronaut.com/posts/2009/01/18/jquery-smooth-scroll-bugs.html#opera, which was written here by Divya Manian.
One need to use event.preventDefault() before calling scroll function or animate function to end click event before start scroll event.
Something like this:
$(<clicked_element>).click(function(event){
event.preventDefault();
$('html, body').animate({scrollTop: <value>}, 600);
});
EDIT: It's important to apply scroll method to $('html, body') elements
I'm measuring the window and document width and height via the following properties :
//measure the window and document height and width dynamically
var w = $(window).width();
var h = $(window).height();
var wd = $(document).width();
var hd = $(document).height();
Works fine in firefox but IE kicks up a fuss. Is there an alternative to this syntax that works in IE?
JS error recieved - could not get the position property. Invalid Argument
Works for me in both FF and IE, check for yourself here.
i just figured out, whats the "bug" in the code.
Firefox is able to get width and height, whereever you put your javascript.
But IE is only able to get this values when the script is within the body element.
I've had the same problem here and was trying about an hour.
I noticed, that the jsbin script is inside the pagebody and moved my javascript into the body and wow - it works in IE :-)
Best regards
I had the same problem and i solve it.
The question was related with IE being in Quircks mode, because i had in the begining of the HTML some non valid tags (i copy the source from a .aspx page, and i left there the <%page ..%> directive.
When IE finds some strange tag it enters quircks mode, and some things work diferent.
When i deleted the strange tag, the $(window).width(); stuff begins to work.
Hope this helps someone in the future with my same problem. :)
I'm building an auto-follow div that is bound to the $(window).scroll() event. Here is my JavaScript.
var alert_top = 0;
var alert_margin_top = 0;
$(function() {
alert_top = $("#ActionBox").offset().top;
alert_margin_top = parseInt($("#ActionBox").css("margin-top"));
$(window).scroll(function () {
var scroll_top = $(window).scrollTop();
if(scroll_top > alert_top) {
$("#ActionBox").css("margin-top", ((scroll_top-alert_top)+(alert_margin_top*2))+"px");
console.log("Setting margin-top to "+$("#ActionBox").css("margin-top"));
} else {
$("#ActionBox").css("margin-top", alert_margin_top+"px");
};
});
});
This code assumes that there is this CSS rule in place
#ActionBox {
margin-top: 15px;
}
And it takes an element with the id "ActionBox" (in this case a div). The div is positioned in a left aligned menu that runs down the side, so it's starting offset is approximately 200 px). The goal is to start adding to the margin-top value once the user has scrolled past the point where the div might start to disappear off the top of the browser viewport (yes I know setting it to position: fixed would do the same thing, but then it would obscure the content below the ActionBox but still in the menu).
Now the console.log shows that the event is firing every time it should and it's setting the correct value. But in some pages of my web app the div isn't redrawn. This is especially odd because in other pages (in IE) the code works as expected (and it works every time in FF, Opera and WebKit). All pages evaluate (0 errors and 0 warnings according to the W3C validator and the FireFox HTMLTidy Validator), and no JS errors are thrown (according to the IE Developer Toolbar and Firebug). One other part to this mystery, if I unselect the #ActionBox margin-top rule in the HTML Style explorer in the IE Developer Tools then the div jumps immediately back in the newly adjusted place that it should have if the scroll event had triggered a redraw. Also if I force IE8 into Quirks Mode or compatibility mode then the even triggers an update.
One More thing, it works as expected in IE7 and IE 6 (thanks to the wonderful IETester for that)
I'm having a problem with your script in Firefox. When I scroll down, the script continues to add a margin to the page and I never reach the bottom of the page. This occurs because the ActionBox is still part of the page elements. I posted a demo here.
One solution would be to add a position: fixed to the CSS definition, but I see this won't work for you
Another solution would be to position the ActionBox absolutely (to the document body) and adjust the top.
Updated the code to fit with the solution found for others to benefit.
UPDATED:
CSS
#ActionBox {
position: relative;
float: right;
}
Script
var alert_top = 0;
var alert_margin_top = 0;
$(function() {
alert_top = $("#ActionBox").offset().top;
alert_margin_top = parseInt($("#ActionBox").css("margin-top"),10);
$(window).scroll(function () {
var scroll_top = $(window).scrollTop();
if (scroll_top > alert_top) {
$("#ActionBox").css("margin-top", ((scroll_top-alert_top)+(alert_margin_top*2)) + "px");
console.log("Setting margin-top to " + $("#ActionBox").css("margin-top"));
} else {
$("#ActionBox").css("margin-top", alert_margin_top+"px");
};
});
});
Also it is important to add a base (10 in this case) to your parseInt(), e.g.
parseInt($("#ActionBox").css("top"),10);
Try marginTop in place of margin-top, eg:
$("#ActionBox").css("marginTop", foo);
I found the answer!
I want to acknowledge the hard work of everyone in trying to find a better way to solve this problem, unfortunately because of a series of larger constraints I am unable to select them as the "answer" (I am voting them up because you deserve points for contributing).
The specific problem I was facing was a JavaScript onScoll event that was firing but a subsequent CSS update that wasn't causing IE8 (in standards mode) to redraw. Even stranger was the fact that in some pages it was redrawing while in others (with no obvious similarity) it wasn't. The solution in the end was to add the following CSS
#ActionBox {
position: relative;
float: right;
}
Here is an updated pastbin showing this (I added some more style to show how I am implementing this code). The IE "edit code" then "view output" bug fudgey talked about still occurs (but it seems to be a event binding issue unique to pastbin (and similar services)
I don't know why adding "float: right" allows IE8 to complete a redraw on an event that was already firing, but for some reason it does.
The correct format for IE8 is:
$("#ActionBox").css({ 'margin-top': '10px' });
with this work.
try this method
$("your id or class name").css({ 'margin-top': '18px' });