-
-
Save ronkorving/3755461 to your computer and use it in GitHub Desktop.
(function (window) { | |
// This library re-implements setTimeout, setInterval, clearTimeout, clearInterval for iOS6. | |
// iOS6 suffers from a bug that kills timers that are created while a page is scrolling. | |
// This library fixes that problem by recreating timers after scrolling finishes (with interval correction). | |
// This code is released in the public domain. Do with it what you want, without limitations. I do not promise | |
// that it works, or that I will provide support (don't sue me). | |
// Author: [email protected] | |
var timeouts = {}; | |
var intervals = {}; | |
var orgSetTimeout = window.setTimeout; | |
var orgSetInterval = window.setInterval; | |
var orgClearTimeout = window.clearTimeout; | |
var orgClearInterval = window.clearInterval; | |
function createTimer(set, map, args) { | |
var id, cb = args[0], repeat = (set === orgSetInterval); | |
function callback() { | |
if (cb) { | |
cb.apply(window, arguments); | |
if (!repeat) { | |
delete map[id]; | |
cb = null; | |
} | |
} | |
} | |
args[0] = callback; | |
id = set.apply(window, args); | |
map[id] = { args: args, created: Date.now(), cb: cb, id: id }; | |
return id; | |
} | |
function resetTimer(set, clear, map, virtualId, correctInterval) { | |
var timer = map[virtualId]; | |
if (!timer) { | |
return; | |
} | |
var repeat = (set === orgSetInterval); | |
// cleanup | |
clear(timer.id); | |
// reduce the interval (arg 1 in the args array) | |
if (!repeat) { | |
var interval = timer.args[1]; | |
var reduction = Date.now() - timer.created; | |
if (reduction < 0) { | |
reduction = 0; | |
} | |
interval -= reduction; | |
if (interval < 0) { | |
interval = 0; | |
} | |
timer.args[1] = interval; | |
} | |
// recreate | |
function callback() { | |
if (timer.cb) { | |
timer.cb.apply(window, arguments); | |
if (!repeat) { | |
delete map[virtualId]; | |
timer.cb = null; | |
} | |
} | |
} | |
timer.args[0] = callback; | |
timer.created = Date.now(); | |
timer.id = set.apply(window, timer.args); | |
} | |
window.setTimeout = function () { | |
return createTimer(orgSetTimeout, timeouts, arguments); | |
}; | |
window.setInterval = function () { | |
return createTimer(orgSetInterval, intervals, arguments); | |
}; | |
window.clearTimeout = function (id) { | |
var timer = timeouts[id]; | |
if (timer) { | |
delete timeouts[id]; | |
orgClearTimeout(timer.id); | |
} | |
}; | |
window.clearInterval = function (id) { | |
var timer = intervals[id]; | |
if (timer) { | |
delete intervals[id]; | |
orgClearInterval(timer.id); | |
} | |
}; | |
window.addEventListener('scroll', function () { | |
// recreate the timers using adjusted intervals | |
// we cannot know how long the scroll-freeze lasted, so we cannot take that into account | |
var virtualId; | |
for (virtualId in timeouts) { | |
resetTimer(orgSetTimeout, orgClearTimeout, timeouts, virtualId); | |
} | |
for (virtualId in intervals) { | |
resetTimer(orgSetInterval, orgClearInterval, intervals, virtualId); | |
} | |
}); | |
}(window)); |
@jordanwilliams33 I have: http://openradar.appspot.com/12756410.
Thank you for this! I am continually getting an error just wondering if you might be able to diagnose it:
TypeError: 'undefined' is not a function (evaluating 'cb.apply(window, arguments)')
Looks like it's happening in the function that gets set during cb = args[0]
this causes libraries such as jquery.transit to not fire their callback.
thanks for the code.
also a newer solution in here
Can anyone confirm is this problem has been resolved in iOS6.1?
I've tried it on iOS6.1, and it seems that the problem has gone away!
Hi.. I have added the fix.js in my header and issue seems to be fixed. But my images are inside a link (anchor tag), while clicking the image, its not redirecting. Just showing the loading icon and nothing happens after that. Please, need help on this :(
You don't need to track timers and intervals separately. You can pass interval IDs to clearTimeout, and the numbers will never overlap, since they use the same underlying list. http://www.whatwg.org/specs/web-apps/current-work/#dom-windowtimers-settimeout
novocaine: requestAnimationFrame isn't a great workaround, since it'll force a much longer delay for small timers (up to 16ms extra).
I wonder what the timing overhead of using a worker comes out to. The onscreen keyboard opening/closing inside a UIWebView (not in Safari) can also cause this issue.
@zewt I figured it wasn't required, but I also didn't want to introduce new cross-browser issues while trying to resolve one. Played it safe there. If all known browsers play nice with this, I guess that could be unified.
Although the bug is fixed in iOS 6.1, is anyone willing to document it on https://github.com/scottjehl/Device-Bugs for posterity? @pixelscript, can yours Radar issue description be used as Device Bugs issue description?
not work for me ;(
I was getting cb.apply is not a function
error. It's because native setTimeout allows you to pass a String, which gets eval'd when the timeout expires. This code doesn't handle that case. I had to make this change to function callback()
:
if (cb) {
if ("string" == typeof cb) {
if ("()" == cb.substring(cb.length - 2, cb.length)) {
cb = cb.substring(0, cb.length - 2)
}
cb = eval(cb);
}
cb.apply(window, arguments);
// etc.
this cods for some reason broke Hammer.js press event, in case any one has the same problem...
You can use web worker to slove this problem, set a timer in your worker js, and postMessage to frontpage.