Javascript window resize possible race condition - javascript

Given the following test case, the expected behaviour is that the lightblue element exactly matches the size of the red parent as the browser window is resized.
<html>
<head>
<script src="https://code.jquery.com/jquery-1.11.1.min.js"/></script>
<style type="text/css">
#viewport{
width: 100%;
height: 30%;
background-color: red;
}
#child{
background-color: lightblue;
}
</style>
</head>
<body>
<div id="viewport"><div id='child'></div></div>
<script>
window.addEventListener( 'resize', function(){
var width = $('#viewport').innerWidth();
var height = $('#viewport').innerHeight();
$('#child').css('height', height+'px');
$('#child').css('width', width+'px');
});
</script>
</body>
</html>
The actual behaviour on chrome 36.0.1985.143 is that sometimes (especially at smaller window sizes) the child element is set exactly 5px less tall and/or 1px less wide than the parent element. The issue stops happening if $('#child').css('width', width+'px'); is commented out.
It has been reported that the issue doesn't occur on firefox.
A similar issue seems to have been picked up on http://thewebivore.com/using-settimeout-win-race-condition-changing-views/ however I haven't been able to mitigate it with a timeout < 10 ms which is not really a solution.

I've plugged your example code into a fiddle and I seem to be having a host of other problems.
However, if I use vh and vw instead of %, I get much better results:
#viewport {
width: 100vw;
height: 30vh;
background-color: red;
}
#child {
background-color: lightblue;
}
fiddle here...

Related

Shrinking sticky sticky header causes flicker in Blink/Webkit at certain scroll positions

I have a sticky header which utilizes IntersectionObserver to gain a class when stuck, which then hides a few elements and reduces the size of the logo. Of course, when the height of the header shrinks, so does the scroll height, and so if you scroll down just enough to shrink the header, it shrinks, then realizes it's no longer stuck so grows, but that cause it to shrink again, so it grows, and so on in an infinite loop. This seems to be most egregious in Chrome, but I've seen it happen in Firefox as well (though Firefox seems to recognize what's happening and sorts itself out).
I've tried numerous things, including a setTimeout() to delay when the class gets removed, adding equivalent margin-bottom to the header when it shrinks, displaying a hidden element with a height of the shrunk space, but nothing seems to fix this problem.
I know I've seen this on other sites before as well, and I suspect this is just a systemic problem with shrinking headers, but is there anything I can do to prevent this from happening? I'm out of ideas.
const OBSERVER = new IntersectionObserver(
([e]) => e.target.classList.toggle("js-is-sticky", e.intersectionRatio < 1),
{
rootMargin: document.getElementById("wpadminbar") ? "-32px 0px 0px 0px" : "0px 0px 0px 0px",
threshold: [1],
}
);
OBSERVER.observe(document.querySelector(".sticky-block"));
CSS and markup is a bit more complicated (and slightly irrelevant), so if needed, please refer to our demo site here. https://gepl.myweblinx.net/
If anything else is needed I'd be happy to add it.
EDIT 1: I see this answer suggests putting a container around the element that retains the correct height, but that won't work with position: sticky; as position: sticky; only works for the closest container (unless someone knows how to get around this?)
EDIT 2: I was overthinking the answer from my first edit
Well, that was a surprisngly obvious solution... Thanks to this answer, I was able to figure out that if I just set a fixed height on the sticky element, but let the contents of that element shrink, the issue goes away.
Essentially:
<div class="sticky-block" style="height:140px;">
<div class="header-block">
...
</div>
<div class="navigation-block">
...
</div>
</div>
In my case these solutions didn't work, Chrome on Android still had the flickering issue. My solution was to make my header position fixed and have a dummy div behind it that resizes to be the same height as the header.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style>
:root {
--lightGrey: #bbbbbb;
}
body {
margin: 0;
width: 100%;
height: 500%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(9, auto);
grid-column-gap: 0px;
grid-row-gap: 0px;
}
header {
width: 100%;
grid-area: 1 / 1 / 1 / 4;
position: fixed;
top: 0;
z-index: 100;
background-color: var(--lightGrey);
}
.headerBackground {
grid-area: 1 / 1 / 1 / 4;
background-color: var(--lightGrey);
height: fit-content;
}
</style>
</head>
<body>
<header>My Header</header>
<div class="headerBackground">Background div</div>
</body>
<script>
// Changes the header once you have scrolled down by 100 pixels or more
$(window).scroll(function () {
if ($(window).scrollTop() >= 100) {
$('header').css('height', '20vw');
$('header').css({ 'font-size': '4vw', 'padding': '5vw' });
} else if ($(window).scrollTop() == 0) {
$('header').attr('style', '');
}
});
// This keeps the space behind the header at the same height as the header to get around the flickering sticky
$(".headerBackground").css({ 'height': ($("header").height() + 'px') });
</script>
</html>

three.js dom element makes the whole page gets larger

I'm trying to build a 3D viewer with three.js, that has full height but leaves space for a side panel. The vertical layout works as expected, but as soon as I append the render's dom element, a horizontal scroll bar appears.
Attached is a minimal working example. I would expect to just see the (black) canvas element and the red body. But after v.append(renderer.domElement), the page gets larger (filled with blue, html element) and a horizontal scroll bar appears. It seems the page is larger than its body.
See https://jsfiddle.net/5jnvt4jh.
Has anybody an idea, what may be happening there? I couldn't find any margin or padding with Chrome and Firefox. Thanks :).
MWE
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<style>
html {
background-color: blue;
}
body {
margin: 0px;
height: 100vh;
background-color: red;
}
#viewer {
height: 100%;
width: 80vw;
background-color: green;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
</head>
<body>
<div id="viewer"></div>
<script>
var v = document.getElementById('viewer');
var renderer = new THREE.WebGLRenderer();
v.append(renderer.domElement);
renderer.setSize(v.clientWidth, v.clientHeight);
</script>
</body>
</html>
Change style of body to:
body {
margin: 0px;
height: 100vh;
background-color: red;
overflow:hidden;
}
jsfiddle: https://jsfiddle.net/raushankumar0717/5jnvt4jh/2/

Full screen width for child element of a non full screen width parent

This question builds upon that one, where in order to apply full screen width to a child of a non full width parent element, the following rule is used on the child element:
width: 100vw;
margin-left: calc(-50vw + 50%);
As shown in this Fiddle, this solution doesn't work in the presence of a vertical scrollbar though: 100vw doesn't take the scrollbar into account and hence the child element ends up being wider than the screen (note: works perfectly without scrollbar).
Is there a way to solve this problem so that the child element takes exactly full screen width? If not in pure CSS then with JS?
Note: an overflow rule on body isn't acceptable in my case as I need the child to fill the exact width of the screen.
https://jsfiddle.net/k3nvkL35/4/
One of the issues you'll come across with the solution here is that I believe scrollbar widths are not universal, and so you may need to implement some conditional logic to affect width/margin based on that.
That being said, you may find this useful. The function below will check to see if the document has a vertical scrollbar by comparing the document's height to the window's height. Based on the existence of said scrollbar, it will modify the child's width and margins to fit the window.
Again, it likely requires tweaking, though it should provide a decent foundation.
function adjustWidth() {
if ($(document).height() > $(window).height()) {
$(".child").css({
"width": "calc(100vw - 18px)",
"margin-left": "calc(-50vw + 50% + 9px)"
});
} else {
$(".child").css({
"width": "",
"margin-left": ""
})
}
}
$(window).resize(adjustWidth).trigger("resize");
.parent {
width: 500px;
height: 500px;
margin: 0 auto;
border: 3px solid green;
}
.child {
height: 100px;
background: red;
width: 100vw;
margin-left: calc(-50vw + 50%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
<div class="child">
content
</div>
</div>
Simple. Since the parent isn't positioned in any way, then the child can be positioned absolutely. No messing with calc or otherwise, works everywhere.
.child {
height: 100px;
background: red;
position:absolute;
left:0;
right:0;
}
Here is a working fiddle:
https://jsfiddle.net/vgbr6qw2/
I found this solution, which was sourced by this guy, but I don't know who originally did it. I haven't fully tested it, so it may not work in all browsers. I know the vw unit isn't supported in IE8, as if anyone cares.
body {
margin:0;
}
.wrapper {
width:100%;
max-width:400px;
margin:0 auto;
background:pink;
/* margin is for display purposes on stack overflows fullscreen snippet view */
margin-top:80px;
}
.full-width {
width:100vw;
margin-left:-50vw;
left:50%;
background:red;
position:relative;
color:white;
}
<div class="wrapper">
<span>Hi. I'm a paragraph.</span>
<div class="full-width">
<p>You aint no paragraph, sucka!</p>
</div>
<strong>Be quiet, you weak losers.</strong>
</div>

CSS keeping image ratio, not working with Chrome

So the main idea is to keep this black image in the top left corner (with a ratio 16:9) and fill the remaining space with a "tomato" div. the solution I had found works fine on FF and even IE but breaks under Chrome and Opera (webkit). I'm not entirely sure what do I need to change..
Here's a link to jsfiddle (it doesn't work there well, so I'm adding the whole code below as well).
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.body{ margin: 0px; }
.container {
position: fixed;
height: 100%;
width: 100%;}
.imgHor {width:100%; display:block;}
.moreSpaceHor {width: 100%;}
.lessSpaceHor {
background: tomato;
height: 100%;}
.imgVer { height: 100%; }
.moreSpaceVer {display: inline; float:top;}
.lessSpaceVer {
#top: 100%;
width: 100%;
float:top;
height:100%;
display: inline;
background: tomato;
position:absolute;
}
</style>
<script>
var timeout=-1;
function manageResizing(){
window.clearTimeout(timeout);
timeout=setTimeout(resizeView,128);
}
function resizeView(){
var img=document.getElementById("imgResizer");
var typeOrient = (9*window.innerWidth > 16*window.innerHeight);
var typeClass = img.className =="imgVer";
//if class and orientation are the same -> quit
if(typeOrient == typeClass) return;
var mSpace=document.getElementById("moreSpace");
var lSpace=document.getElementById("lessSpace");
img.className = typeClass ? "imgHor" : "imgVer";
lSpace.className = typeClass ? "lessSpaceHor" : "lessSpaceVer";
mSpace.className = typeClass ? "moreSpaceHor" : "moreSpaceVer";
}
</script>
</head>
<body class="body" onload="manageResizing();" onresize="manageResizing();">
<div class="container">
<div id="moreSpace" class="moreSpaceHor">
<div style="position:fixed; top:0px; color:white;">PIOTR</div>
<img class="imgHor" id="imgResizer" src="http://oi62.tinypic.com/j9vsj7.jpg"></img>
</div>
<div id="lessSpace" class="lessSpaceHor">KOZAK</div>
</div>
</body>
</html>
//edit
Some more info, it kind of work on Chrome/Opera, it breaks when you try to resize horizontally.. although when you refresh the window it's back as it should be :/
I get an output similar to yours by calling the javascript outside of an onReady() block. Make sure the javascript is called after the document has loaded.
Ok it's been solved now, I followed the idea here about attaching a new method responsible for making Chrome to redraw the page:
Force DOM redraw/refresh on Chrome/Mac
var forceRedraw = function(element){...}
Now it's fine among all browsers.

Firefox scrollTop reset on zIndex change

Whenever I change zIndex of given div (even to the same value) its scrollTop property resets and it scrolls back to start. This causes a very ugly effect to my site (slowdown and black rectangle over the entire div while it repaints). After scrollTop reset I can set it back, but this causes yet another ugly repaint.
This FF bug is driving me mad! I submitted a bug in mozilla https://bugzilla.mozilla.org/show_bug.cgi?id=623937 but they don't care to work on it! Please, if somebody knows how to workaround this issue with some javascript magic? Maybe play with the HTMLElement prototype to somehow override scrollTop?
Here goes a little test case"
<html>
<head>
<style type="text/css">
div#parentDiv
{
position: absolute;
top: 10px;
left: 10px;
width: 300px;
height: 200px;
background-color: green;
}
div#elementToScroll
{
position: absolute;
top: 40px;
left: 40px;
width: 200px;
height: 100px;
overflow-y: hidden;
background-color: blue;
color: white;
}
</style>
</head>
<body>
<div id="parentDiv">
This is the parent DIV
<div id="elementToScroll">This is the child div with overflow-y: hidden and content a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of a lot of ; </div>
</div>
<script>
var parentDIV = document.getElementById("parentDiv");
var elementToScroll = document.getElementById("elementToScroll");
alert ("elementToScroll.scrollTop initial: " + elementToScroll.scrollTop);
elementToScroll.scrollTop = 20;
alert ("We set elementToScroll.scrollTop = 20: current value is: " + elementToScroll.scrollTop);
parentDIV.style.zIndex = 0;
alert ("We set parentDIV.style.zIndex and now elementToScroll.scrollTop is: " + elementToScroll.scrollTop);
</script>
</body>
</html>
I tested and nothing is wrong with Firefox 4:

Categories

Resources