I'm using the library FlipClock.js to build an analog-style clock that uses a version of the CSS "card flip effect." Unfortunately, only after building out my project did I notice a long-standing bug affecting Internet Explorer 9 and below:
https://github.com/objectivehtml/FlipClock/issues/7
In IE9 and below, the clock time lags by one second (i.e., in the first second of animation, nothing visible changes), and the digits in the clock are also offset by a value of 1. My expectation is not for the CSS flip animation to work, but for the digits to change instantly as they currently do, only be correct.
I've been troubleshooting for a while but have yet to find a solution or even pinpoint the problem. I have a hunch that this isn't a script-related bug; I suspect that the digits are changing correctly in the HTML (hard to verify with IE's developer tools), but that they're just not showing as intended due to one or more CSS rules that were written not considering IE9's poor CSS3 support. I'm kind of expecting (or at least hoping) to stumble upon a CSS property or two that just fixes it.
The library is based on a proof-of-concept, which exhibits the same problem:
http://codepen.io/ademilter/pen/czIGo
I'm troubleshooting there to keep it simple, and if I find a fix, will submit a pull request to the FlipClock.js library.
I would greatly appreciate any help!
After removing the CSS animations and shadows, changing the z-index of li.before might do the trick (see http://codepen.io/cbuckley/pen/rysja):
body.play ul li.before {
z-index: 1; /* was previously 3 */
}
So you could use z-index: 1 by default, then feature-detect for CSS animations in the JavaScript and add a body class (say body.supports-animation). Then the relevant CSS might look like:
body.play ul li.before {
z-index: 1;
}
body.play.supports-animation ul li.before {
z-index: 3;
}
/* Prefix animation/background declarations with body.supports-animation too */
Caveat: I haven't tried this with FlipClock, nor have I actually tested on a browser without animation support, but I hope it gives one possible option :-)
Just changing the z-index will fix the problem for IE8 and IE9 but will break the transition for all modern browsers.
To Target specifically I8 or IE9 you can use this:
On your JS file add:
var doc = document.documentElement;
doc.setAttribute('data-useragent', navigator.userAgent);
then on your css you can detect IE8 and IE9 with this code (flipclock.css line 160):
/* PLAY */
.flip-clock-wrapper ul.play li.flip-clock-before {
z-index: 3;/*Original */
}
/*ie8 and ie9 fixes*/
html[data-useragent*='MSIE 8.0'] .flip-clock-wrapper ul.play li.flip-clock-before {
z-index: 1;
}
html[data-useragent*='MSIE 9.0'] .flip-clock-wrapper ul.play li.flip-clock-before {
z-index: 1;
}
That fixed the problem for me!
Is it possible to detect CSS support by using Javascript?
For example, is it possible to detect if the browser supports attribute selectors like this?
input[type='text'] { }
Modernizr is designed to detect browser features and may well be able to help in this instance.
http://www.modernizr.com/
This is a bit speculative as I haven't tested it out, but I believe it would be possible via JS to add a style element followed by an element that it has an effect on, and then test the values:
Speculative untested code, may or may not work (jQuery used for brevity):
$('<style type="text/css" id="foo">input[type="text"]{ width: 10px; }</style>').appendTo('head');
$('<input type="text" id="bar">').appendTo('body');
if ($('#bar').width() == 10)
{
//attr selector supported
}
$('#foo, #bar').remove();
document.querySelectorAll("input[type='text']")
But that fails for older browsers, naturally.
Other than that, you could just use the style property to check if a certain CSS property has been applied or not.
input[type='text'] {
background-repeat: no-repeat; /* or any other irrelevant, non-default value */
}
and
if (myInputElem.style.backgroundRepeat == "no-repeat") {
// selector is supported
}
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()