Convert vh units to px in JS - javascript

Unfortunately 100vh is not always the same as 100% browser height as can be shown in the following example.
html,
body {
height: 100%;
}
body {
overflow: scroll;
}
.vh {
background-color: blue;
float: left;
height: 50vh;
width: 100px;
}
.pc {
background-color: green;
float: left;
height: 50%;
width: 100px;
}
<div class="vh"></div>
<div class="pc"></div>
The issue is more pronounced on iPhone 6+ with how the upper location bar and lower navigation bar expand and contract on scroll, but are not included in the calculation for 100vh.
The actual value of 100% height can be acquired by using window.innerHeight in JS.
Is there a convenient way to calculate the current conversion of 100vh to pixels in JS?
I'm trying to avoid needing to generate dummy elements with inline styles just to calculate 100vh.
For purposes of this question, assume a hostile environment where max-width or max-height may be producing incorrect values, and there isn't an existing element with 100vh anywhere on the page. Basically, assume that anything that can go wrong has with the exception of native browser functions, which are guaranteed to be clean.
The best I've come up with so far is:
function vh() {
var div,
h;
div = document.createElement('div');
div.style.height = '100vh';
div.style.maxHeight = 'none';
div.style.boxSizing = 'content-box';
document.body.appendChild(div);
h = div.clientHeight;
document.body.removeChild(div);
return h;
}
but it seems far too verbose for calculating the current value for 100vh, and I'm not sure if there are other issues with it.

How about:
function viewportToPixels(value) {
var parts = value.match(/([0-9\.]+)(vh|vw)/)
var q = Number(parts[1])
var side = window[['innerHeight', 'innerWidth'][['vh', 'vw'].indexOf(parts[2])]]
return side * (q/100)
}
Usage:
viewportToPixels('100vh') // window.innerHeight
viewportToPixels('50vw') // window.innerWidth / 2

The difference comes from the scrollbar scrollbar.
You'll need to add the height of the scrollbar to the window.innerHeight. There doesn't seem to be a super solid way of doing this, per this other question:
Getting scroll bar width using JavaScript

Related

Changing Height in CSS with Variable in JavaScript

So I'm trying to make the height of my DIV's respond to the width of the window. The code:
var w = window.innerWidth;
var mathW = parseInt(w);
var divHeight = mathW*0.16;
document.getElementById('demoDiv').style.height = divHeight;
With the CSS being:
#demoDiv {
margin: auto;
background-color: red;
width: 100%;
height: 50px; //just to start with SOMETHING
}
Obviously I'm having trouble with that! I've played around with it and definitely can change the height if I just use regular integers, but even using parseInt('divHeight') did nothing for me!
You are calucalting the height in a correct way. But in order to set the height you need to add dimension to the value (say px).
You dont need to do parseInt as window.innerWidth is a property with typeof number.
var w = window.innerWidth;
var divHeight = mathW*0.16;
document.getElementById('demoDiv').style.height = divHeight+"px";
What stops you from applying a pure CSS solution?
#demoDiv {
height: 16vw;
}
Requires no Javascript, is much shorter and performs faster, and no need to apply any resize handler when window size changes.

Delay or specified width on body needed for correct rendering

(This is a follow-up on my previous question if anybody is interested in the background story for entertainment purposes. It will probably not help you understand this question.)
Here are two elements <aside> and <main> who have got their width and height via JavaScript so that their combined width is the width of your screen (note that their display is inline-block). If you run this code in your web browser (a maximized browser so that the width of your browser equals the width of your screen) you might note that the body surprisingly does not properly fit the elements:
<!DOCTYPE html>
<html>
<body>
<aside></aside><!-- comment to remove inline-block whitespace
--><main></main>
<script>
var h = screen.height/100;
var w = screen.width/100;
var e = document.getElementsByTagName("aside")[0].style;
e.display = "inline-block";
e.backgroundColor = "lightblue";
e.width = 14*w + "px";
e.height = 69*h + "px";
e.marginRight = 0.5*w + "px";
e = document.getElementsByTagName("main")[0].style;
e.display = "inline-block";
e.backgroundColor = "green";
e.width = 85.5*w + "px";
e.height = 69*h + "px";
e = document.getElementsByTagName("body")[0].style;
e.margin = e.padding = "0";
e.backgroundColor = "black";
</script>
</body>
</html>
If you however give the JavaScript a delay, the elements are rendered properly. This suggests that the body somehow "needs time" to figure out its correct width:
<script>
setTimeout(function() {
[...]
}, 200);
</script>
It is also possible to give the body the specified width of screen.width instead of introducing the delay, by adding the following line. This supports the previous guess that the body does not immediately know its correct width (unless specified):
<script>
[...]
e.width = 100*w + "px";
</script>
Even though I have taken the freedom to throw wild guesses to explain this, I do not actually have a clue to what is going on.
Why are the elements not placed properly in the first place, and why do these two solutions work?
(Note: It is also possible to fix this by setting the whitespace of the body to nowrap with e.whiteSpace = "nowrap";, but I suspect this does not do the same thing as the other two. Instead of creating space for the elements inside the body, this simply forces the elements to be next to each other even though there is not enough room in the body.)
You should wait for the DOM to be available before running your code, see here: pure JavaScript equivalent to jQuery's $.ready() how to call a function when the page/dom is ready for it. That is possibly why setTimeout works. Also you should assign seperate variable names for your different elements.
// self executing function before closing body tag
(function() {
// your code here
// the DOM will be available here
})();
Is there a reason you are using Javascript and not CSS to accomplish this task? I suggest giving your elements css ids ie id="aside", then set your css styles:
html,body {
width: 100%;
height: 100%;
}
#aside {
display:inline-block;
position: relative;
float: left;
width: 14%;
height: 69%;
background: blue;
}
#main {
display:inline-block;
position: relative;
float: left;
width: 86%;
height: 31%;
background: azure;
}

make div 100% of browser height depending on length of content

I have 2 divs, a navigation and a main content in a bootstrap grid system. The length of either can vary depending on amount of content. I need them both styled to fill 100% of the browser window IF neither has the content to reach the bottom naturally. But if at least one of the divs has more content than the length of the browser window, I need to be able to scroll down the page with the styles of both divs remaining in tact and both have a height of the longer of the 2 divs.
I'm currently using a javascript resize function which seems to work but not in the case where neither div is long enough to fill the height of the browser window. Any suggestions?
HTML
<div class="row">
<div id="nav" class="col-xs-2">
Variable Height Navigation
</div>
<div id="main" class="col-xs-10">
Variable Height Content
</div>
</div>
Javascript
function resize() {
var h = (document.height !== undefined) ? document.height : document.body.offsetHeight;
document.getElementById("nav").style.height = h + "px";
}
resize();
window.onresize = function () {
resize();
};
I am trying to understand you question, and if I'm correct what you are looking for is:
Both divs need to be equally high
They need be at least the height of the screen
They need to take the height of the highest div
So let's try to achieve this goal as simply as possible:
var main = document.getElementById('main');
var nav = document.getElementById('nav');
function resize(){
var highest;
// Set the divs back to autosize, so we can measure their content height correctly.
main.style.height = 'auto';
nav.style.height = 'auto';
// Find the highest div and store its height.
highest = main.clientHeight > nav.clientHeight
? main.clientHeight
: nav.clientHeight;
// Check if the highest value is the div or the window.
highest = highest > window.innerHeight
? highest
: window.innerHeight;
// Assign the newly found value
main.style.height = highest + 'px';
nav.style.height = highest + 'px';
}
resize();
// Also, you don't need to wrap it in a function.
window.resize = resize;
// However, better would be:
window.addEventListener('resize', resize, false);
#main, #nav {
width: 100%;
height: auto;
}
#main { background: red; }
#nav { background: green; }
<div id="main"></div>
<div id="nav"></div>
Now, If you aren't bothered with the actual sameness in heiught of both divs but just want them to at least be one screenful, you should consider using CSS:
html, body { height: 100%; }
#nav, #main { min-height: 100%; }
I think that is the better solution (no Javascript!) and sort-of does what you want, bar the fact that you won't have to equally high div elements. However, you would barely notice it as each will at least fill the page.
You could try using viewport height:
For example:
#nav {
min-height: 100vh;
}
#main {
min-height: 100vh;
}
See Bootply.
This will also remove the need for JavaScript.

Programmatically Resizing Divs

I'm working on an HTML5 browser game that can be divided into 3 parts: two UI panels on the left and right of a center set of square canvases for the playing surface. The three panels need to be horizontally aligned, and the total game needs to keep an aspect ratio of 16:9. The left and right panels should be of equal widths, and all three panels must be of equal height. I have specified a minimum width and height inside a resize() function called when an onresize event is detected.
Currently, each panel is a div, and all three are contained inside a section. Right now, the section isn't necessary, but I want to keep the game separated from extra content at the bottom of the screen that I might choose to add later.
The CSS style is as follows:
* {
vertical-align: baseline;
font-weight: inherit;
font-family: inherit;
font-style: inherit;
font-size: 100%;
border: 0 none;
outline: 0;
padding: 0;
margin: 0;
}
#gameSection {
white-space: nowrap;
overflow-x: hide;
overflow-y: hide;
}
#leftPanel, #centerPanel, #rightPanel {
display: inline-block;
}
#leftPanel {
background-color: #6495ed;
}
#centerPanel {
background-color: #e0ffff;
}
#rightPanel {
background-color: #b0c4de;
Right now, I have set the background color of each div just to show me when I'm correctly setting the size of each div.
The body of my HTML document is as follows:
<body onresize="resize()">
<section id="gameSection">
<div id="leftPanel">Left Panel.</div>
<div id="centerPanel">Center Panel.</div>
<div id="rightPanel">Right Panel.</div>
</section>
</body>
And finally, my resize() function (I created a separate function for resizing the game in case I add more elements below later):
function resize() {
var MIN_GAME_WIDTH = 800;
var MIN_GAME_HEIGHT = 450;
var GAME_ASPECT_RATIO = 16 / 9;
var width = window.innerWidth;
var height = window.innerHeight;
var gWidth, gHeight;
if(width < MIN_GAME_WIDTH || height < MIN_GAME_HEIGHT) {
gWidth = MIN_GAME_WIDTH;
gHeight = MIN_GAME_HEIGHT;
}
else if ((width / height) > GAME_ASPECT_RATIO) {
<!-- width is too large for height -->
gHeight = height;
gWidth = height * GAME_ASPECT_RATIO;
}
else {
<!-- height is too large for width -->
gWidth = width;
gHeight = width / GAME_ASPECT_RATIO;
}
resizeGame(gWidth, gHeight, GAME_ASPECT_RATIO);
}
function resizeGame(var gWidth, var gHeight, var aspectRatio) {
var gSection = document.getElementById("gameSection");
var lPanel = document.getElementById("leftPanel");
var cPanel = document.getElementById("centerPanel");
var rPanel = document.getElementById("rightPanel");
gSection.height = gHeight;
gSection.width = gWidth;
<!-- should the below be taken care of in the CSS? -->
lPanel.height = gHeight;
cPanel.height = gHeight;
rPanel.height = gHeight;
cPanel.width = cPanel.height;
lPanel.width = (gWidth - cPanel.width) / 2;
rPanel.width = lPanel.width;
}
I've tried a number of different commands to resize the divs, but it just isn't working for me. When I try adding test canvases, color appears, but the boxes still aren't the correct size. I have also considered loading an invisible background image to each div and scaling it to the desired size; however, I was able to resize my canvas using the above method before and it seemed to work just fine.
Additional Notes
While I've already had pretty good success resizing a single canvas, I don't want to use just one canvas for the game because not all parts of the UI need to be drawn at the same time.
I'm trying to keep this solely in Javascript.
I suspect that I could just use CSS to handle resizing by fixing the aspect ratio to 16:9 and using width:56.25% for the center panel and width:21.875% for the side panels, but that limits me to one aspect ratio and doesn't explain why my above script isn't working.
I can provide the entire HTML file if needed. This is what it's supposed to look like:
End Goal (without right panel)
Thank you!
UDPATE:
jsfiddle
I got it kind of working here. I made a lot of changes/minor fixes to the code before finding what was wrong (other than various syntax errors):
You were using .width and .height instead of .style.width and .style.height, and you were applying integers to these instead of strings with "px" appended to them. Both of these things are completely understandable to miss.
I also moved the onresize from the body tag into the JS, don't know why it wasn't working on jsfiddle, but this is good practice anyways.
In the future: learn how to debug JS using the console and when you ask questions, use small examples, not your entire codebase. This question could have been simplified to "How do I resize a div?" with one line of JS and one div. You also should consider not doing this specific thing in JS, and using flexbox as redbmk said.

How to implement max height & scrollbar with YUI Panel?

I'm using the YUI Panel element that is rendered via an AJAX call. Is there a way to set a maximum height and use a scrollbar whenever the content of the panel exceeds that height?
Definitely! Here is an example with a Dialog with id dlg:
var dialog = new YAHOO.widget.Dialog("dlg", { fixedcenter : true....
So then set the bd css class to auto overflow:
#dlg .bd {
overflow: auto;
}
And then in your js put something like:
YAHOO.util.Dom.setStyle(dialog.body, "height", "300px");
You'll probably need to add some styling to the ft class also, but that's up to you:
#dlg .ft {
padding-top: 10px;
border-top: 1px solid #CECECE;
}
And if you don't care about a specific height, but just want to avoid it being larger than the window then the following code handles that.
myDialog.renderEvent.subscribe(function() {
// Find .yui-panel .bd
let dialogElement = $('.yui-panel .bd');
// set the max-height to the 90% of the screen
let windowHeight = $(window).height();
dialogElement.css('max-height', windowHeight*0.9);
// set overflow:auto
dialogElement.css('overflow', 'auto');
});

Categories