I have quite a complex page (responsive) with a lot of scripts running, and rather than trying to recalculate all these scripts when the browser is resized, I simply wish to trigger a refresh when the browser is resized.
Trouble is, that a refresh on browser resize sends Android devices, and some versions of IE into a refresh loop. I was thinking therefore to only refresh the browser after it has been resized a certain amount, and see if that cures the problem, which is what I'm attempting to do below. Though for some reason, the browser won't refresh at all. The counter works fine, but the window.location.reload(); doesn't appear to be working here (works outside of this function fine). Any ideas as to why this is?
var $resizeTolerance = 0;
jQuery(window).on("resize", function(){
$resizeTolerance++;
console.log($resizeTolerance);
if($resizeTolerance > 10) {
window.location.reload();
}
});
I would strongly recommend not doing this, not least because you've said you don't want to "...[try] to recalculate all these scripts when the browser is resized..." but of course that's exactly what you're doing: When the page is reloaded, you're repeating all the calcuations. Instead, make sure the calculations are repeatable, then repeat them.
But with that out of the way:
What you're looking for is setTimeout, to do something after the user is done resizing the browser:
var loadedAt = Date.now();
var resizeHandle = 0;
jQuery(window).on("resize", function(){
if (Date.now() - loadedAt < 300) {
// ignore
return;
}
if (resizeHandle) {
clearTimeout(resizeHandle);
}
resizeHandle = setTimeout(reloadPage, 100); // 100ms = 1/10th second
});
function reloadPage() {
window.location.reload();
}
That will wait until a tenth of a second after the last resize event occurs, and then trigger a page refresh. You may want to adjust the time.
It also ignores any resize event within 300ms of when the code hooks up the handler, to ignore the initial resize event the browser apparently sends.
Live Example
Side note: This uses Date.now, which nearly all browsers have at this point. You can shim it in older browsers like this:
if (!Date.now) {
Date.now = function() {
return +new Date();
}
}
Related
I'm hoping someone can explain to me this odd behavior I'm seeing with window.scrollTo.
This doesn't work.
document.addEventListener('DOMContentLoaded', function() {
console.log('window.scrollY = ', window.scrollY);
window.scrollTo(0, 200)
console.log('window.scrollY = ', window.scrollY);
});
... well it does work, sort of ...
It works for the initial page load, but not refreshes (cmd + shift + r)...
The console output is:
window.scrollY = 0
window.scrollY = 200
So in that sense its working... except the page isn't scrolled, and when you type window.scrollY into the dev console it does indeed show 0.
So it would appear that the scroll is being set too early?
Where I get real confused is here:
var delay_ms = 0;
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
window.scrollTo(0, 200);
}, delay_ms);
});
SOMEHOW THAT WORKS, but not consistently.
However...
var delay_ms = 10;
Increasing the delay, even by only 10ms, improves the consistently dramatically! To the point where it hasn't failed on me yet.
At first I thought maybe DOMContentLoaded was simply too early for height to be properly evaluated, so I switched the event I was listening for to:
window.addEventListener('load', function() { /* ... */ });
According to the MDN Docs for .onload
The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading.
So I can't imagine what would be setting the scroll to 0, after I'm setting it to 200...
Does anyone have any insight into what is happening with this timing?
Covering my bases
Yes there is space to scroll.
Here's a gist with some full reproduction code.
I'm on a mac, using chrome.
So I am trying to find the height of my images then add a top margin this enables me to impose a a vertical center.
I'm running this code, and on an F5 refresh I get correct height but on CTRL+F5 refresh it gives me a much smaller height. I kind of assume this is a loading/delay thing, but I am using document ready so not really sure whats going on. I tried using a php function but it slows the site down amazingly so have to stick with jquery.
you can see it working here. www.mzillustration.com
jQuery(document).ready(function() {
if (jQuery('.imagedisplay').length != 0) {
jQuery('.imagedisplay').each(function(){
var imgheight = jQuery(this).find('img').height();
var topmarg = ((240 - imgheight) / 2) ;
jQuery(this).find('img').css({'margin-top':topmarg+'px'});
});
});
any ideas/help/explanation much appreciated.
thanks
There is a difference between onload and onready.
ready will wait until the actual DOM-tree is done, while onload will wait until ALL of the content displayed on the page is finnished loading. So an explanation would be that when clearing the cache and refreshing, the dom tree finishes much faster than the images, hence giving the wrong heigh.
Try using the onload-event instead and see if you get a different result.
You need to insure the image has loaded before asking the browser for its height. If that image path is living in the html you will unfortunately need a jquery pluggin to handle this in a cross browser manner.
https://github.com/alexanderdickson/waitForImages
http://desandro.github.com/imagesloaded/
Or you will have to wait for the window.onload event which in jquery looks like this:
$(window).on('load', function(){....
However if you use the window load event, it will wait until ALL resources have loaded and depending on your site that can be a serious delay when compared to measuring just the image itself.
Or if you are comfortable with loading the image from javascript, simply ordering your code properly will handle this:
var loadTester = new Image(),
imgH;
$(loadTest).on('load',function(){
imgH = $('#image').attr('src',loadTester.src).height();
}
loadTester.src = "paht/to/image.jpg";
The reason you are seeing a difference in the manner you reload the page, is that a simple refresh does not clear the cache, so the image is already loaded. When you hit ctrl+f5 it clears the cache and so the image is not yet loaded when you ask the browser for the height.
For cache control durring development consider getting the firefox web-developer toolbar.
Try this approach:
jQuery(function() {
jQuery('.imagedisplay img').each(function() {
var $this = jQuery(this),
height = $this.height();
if (height) {
$this.css('margin-top', ((240 - height) / 2) + 'px');
} else {
$this.on('load', function() {
$this.css('margin-top', ((240 - $this.height()) / 2) + 'px');
});
}
});
});
images are/can be cached/loaded separately from the actual page content. the document being ready can (and in my experience usually) occurs before everything is loaded.
try adding an event listener to the actual element being loaded.
You need to make sure the image has loaded before extracting a height. You can easily check this using the complete property on the image. Try this:
var setH = function() {
$(this).css('margin-top', (240 - this.height) / 2);
}
$('.imagedisplay img').each(function() {
if( this.complete ) {
setH.call(this); // apply height straight away
return;
}
$(this).load(setH); // apply height when the image has loaded
});
I have a piece of code:
var logo = $("#blinking-logo");
function logo_blink() {
logo.fadeOut(10).delay(10)
.fadeIn(10).delay(20)
.fadeOut(10).delay(10)
.fadeIn(10)
window.setTimeout(logo_blink, (Math.random()*(1500))+1500);
}
logo_blink();
All it makes is blinking a picture once in ~30 seconds (time is less here for easier debugging)
The problem that Chrome pauses this timer while the tab in backgrounded, and then, when coming back to that tab, it blinks all the blinks that were missed in background.
I'd like to pause the timer while in background, but I don't know how. I've read some related posts, but it seems that they describe the opposite problem. Is there any way to detect the backgrounding of a tab?
It is a known feature. To conserve the resources Chrome does not update the window without focus :) You could check, for example, that window lost its focus and stop the timer. Start it again when window is in focus. For example:
var timer = null;
var logo = $("#blinking-logo");
function logo_blink() {
if(timer) clearTimeout('timer');
logo.fadeOut(10).delay(10)
.fadeIn(10).delay(20)
.fadeOut(10).delay(10)
.fadeIn(10)
timer = window.setTimeout(logo_blink, (Math.random()*(1500))+1500);
}
logo_blink();
$(window).blur(function(){clearTimeout(timer); timer = null;});
$(window).focus(function(){if (!timer) timer = window.setTimeout(logo_blink, (Math.random()*(1500))+1500);});
Something like this. On one of my pages with animation a had the same problem with setInterval, so I just pause it when the page is in background.
if (!$.browser.msie)
{
$(window).focus(function(){paused = false;});
$(window).blur(function(){paused = true;});
}
And than skipped animation based on the value of paused flag.
ps: Code is updated with optimization discussed below.
Chrome, Firefox and IE10 have page visibility APIs that you can use to determine when you page is no longer visible. This works better than using focus in some circumstances. Here's an example from MDN:
//startSimulation and pauseSimulation defined elsewhere
function handleVisibilityChange() {
if (document.hidden) {
pauseSimulation();
} else {
startSimulation();
}
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
And, some reference documents:
http://code.google.com/chrome/whitepapers/pagevisibility.html
https://developer.mozilla.org/en/DOM/Using_the_Page_Visibility_API
W3 Document: http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html
The only work-arounds I've seen for older browsers involve determining whether your window has focus or not which isn't perfect, but maybe better than nothing in some cases.
I'd like to know when the user first does some scrolling on my page, as distinct from the browser-initiated scrolling that happens automatically when you reload a page.
If I try capturing the window's initial scroll position, and then registering an onscroll handler to tell me the scroll position has changed, I don't get too far: browser-initiated scrolling happens after document ready (jQuery's definition), so window.pageYOffset is always 0 on doc ready, even if the browser's right about to jump me down a hundred pixels.
If I try inspecting the onscroll event, nothing seems to let me distinguish a user-initiated event object from a browser-initiated one. The two events have pretty identical properties.
I'm looking for something a little more robust than what's suggested here: How to distinguish scrolling by mouse from scrolling programmatically in JavaScript?.
Thanks...
It's ugly, but possibly the best way to tell is by timing. I notice that on Chrome, the change in pageYOffset happens within 1 millisecond of the window.onload, while on Firefox, it happens within one millisecond of the dom loading. (haven't tested IE, but it is likely that one or the other works) For instance I added this to the bottom of a page:
<script>
window.onload = function () {
var first = window.pageYOffset;
setTimeout(function () {
second = window.pageYOffset;
alert("first: " + first + ", second:" + second)
}, 1);
};
</script>
One chrome, "first" is 0, "second" is a big number, when refreshing a scrolled page. On firefox, that is only true if the below is added right before the closing body tag (which should be the same as jquery's document.ready).
<script>
var first = window.pageYOffset;
setTimeout(function () {
second = window.pageYOffset;
alert("first: " + first + ", second:" + second)
}, 1);
</script>
I think you could detect a "browser refresh initiated" scroll reliably using this sort of technique, but obviously, it's not what I'd call a pretty solution and it might break on a future browser version.
I use a similar technique for a scroll behavior on our home page, not the most elegant but it works well:
<html>
<head></head>
<body>
<div style="height:500px;line-height:500px">
<a id="bm" href="#bm">bookmarked</a>
</div>
<script>
(function(){
var scrollingCanStart = false;
//the user interact with the page
window.onmouseover = window.onkeydown = function(e){
scrollingCanStart = true;
};
//onscroll action
window.onscroll = function(e){
if(!scrollingCanStart){
return;
}
console.log('manual scroll');
};
})();
</script>
</body>
</html>
i was solving the same task and would like to share my approach.
my assumptions:
the user's scroll always preŃeded with events like 'wheel',
'another user's scroll' and etc
all events have timeStamp
I have chosen rxjs library for implementation
// this stream will contain last User's scroll
var last_scroll = new Rx.Subject();
var start = rx.Observable.merge(
Rx.Observable.fromEvent(element, 'wheel'),
last_scroll
)
.map(function (e) { return e.timeStamp; });
Rx.Observable.fromEvent(element, 'scroll')
.withLatestFrom(start, function (scroll, start) {
return {scroll: scroll, start: start};
})
.filter(function (o) {
return Math.abs(o.scroll.timeStamp - o.start) <= 500;
})
.map(function (o) {
return o.scroll;
})
.do(function (ev) {
last_scroll.onNext(ev);
})
.subscribe(function (ev) {
// here we have scroll event initiated by user
});
Please feel free to improve this code and share your thoughts. Happy coding!
I assigned a timeout to my window.resize handler so that I wouldn't call my sizable amount resize code every time the resize event fires. My code looks like this:
<script>
function init() {
var hasTimedOut = false;
var resizeHandler = function() {
// do stuff
return false;
};
window.onresize = function() {
if (hasTimedOut !== false) {
clearTimeout(hasTimedOut);
}
hasTimedOut = setTimeout(resizeHandler, 100); // 100 milliseconds
};
}
</script>
<body onload="init();">
...
etc...
In IE7 (and possibly other versions) it appears that when you do this the resize event will constantly fire. More accurately, it will fire after every timeout duration -- 100 milliseconds in this case.
Any ideas why or how to stop this behavior? I'd really rather not call my resize code for every resize event that fires in a single resizing, but this is worse.
In your //do stuff code, do you manipulate any of the top,left,width,height,border,margin or padding properties?
You may unintentionally be triggering recursion which unintentionally triggers recursion which unintentionally triggers recursion...
How to fix the resize event in IE
also, see the answer for "scunliffe" "In your ... properties?
IE does indeed constantly fire its resize event while resizing is taking place (which you must know, as you are already implementing a timeout for a fix).
I am able to replicate the results you are seeing, using your code, on my test page.
However, the problem goes away if I increase the timeout to 1000 instead of 100. You may want to try with different wait values to see what works for you.
Here is the test page I used: it has a nicely dynamic wait period already set up for you to play with.
I stumbled on the same problem, but solved it differenly, and I think it's more elegant than making a timeout....
The context: I have an iframed page, loaded inside the parent page, and the iframe must notify the parent when its size changes, so the parent can resize the iframe accordingly - achieving dynamic resizing of an iframe.
So, in the iframed HTML document, I tried to register a callback on the body tag. First, on the onchange - it didn't work. Then on resize - it did work, but kept firing constantly. (Later on I found the cause - it was apparently a bug in Firefox, which tried to widen my page to infinity). I tried the ResizeObserver - for no avail, the same thing happened.
The solution I implemented was this:
<body onload="docResizePipe()">
<script>
var v = 0;
const docResizeObserver = new ResizeObserver(() => {
docResizePipe();
});
docResizeObserver.observe(document.querySelector("body"));
function docResizePipe() {
v += 1;
if (v > 5) {
return;
}
var w = document.body.scrollWidth;
var h = document.body.scrollHeight;
window.parent.postMessage([w,h], "*");
}
setInterval(function() {
v -= 1;
if (v < 0) {
v = 0;
}
}, 300);
</script>
So how it works: each time the callback fires, we increment a variable v; once in every 300 ms, we decrement it; if it's too big, the the firing is blocked.
The big advantage of this over the timeout-based solution, is that it introduces to lag for a user experience, and also clear in how exactly it does block the recursion. (Well, actually not )))