Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elements do not find enough space inside body - JavaScript styling

Relevant information:

The page contains two elements:

  • An <aside> to the left.

  • A <main> to the right.

(Note: Throughout this post, heights are mentioned for the sake of completeness, but are irrelevant to producing this problem.)

All heights, widths, and margins are set with respect to var w = screen.width/100; (and var h = screen.height/100;) so that the page essentially looks the same in any display resolution. And they are set so that the width of <aside> and <main>, and the margin between them all add up to screen.width.

For example:

var w = screen.width/100;
document.getElementsByTagName('main')[0].style.width = 85.5*w + "px";
document.getElementsByTagName('aside')[0].style.width = 14*w + "px";
document.getElementsByTagName('aside')[0].style.marginRight = 0.5*w + "px";

(85.5 + 14 + 0.5 = 100)

The problem:

The <main> gets pushed down below the <aside> for unknown reasons. I can only think of a half-sensible hypothesis to somewhat explain this behavior.

However, if I set the font size of the body to 0 and zoom out (so that the elements take less space) and zoom back in, this gets fixed (I don't know why, and don't ask me how I found this out).

What is the reason for this behavior, and what is the proper fix?

The hypothesis (can be skipped):

The browser seems to think "What would happen if I display the scrollbars even though they are not needed?", and then notices that the scrollbars have a width > 0, which means that <aside> and <main> are taking more space than available (since they are set to take up 100% of the screen width, and now there is a scrollbar competing for the space). The browser therefore decides to reposition the <main> below the <aside> and ruin the design.

And now since <main> is under <aside> the elements no longer fit inside the screen and the scrollbars are actually needed now and therefore stay, even though they are the cause of their own existence (as far as this hypothesis goes).

Additional information:

  • I am not using any CSS-stylesheet: all my styling is done by JavaScript (for the simple reason that I want the sizes to depend on screen.width and screen.height).

  • The elements have display = "inline-block";. Using float produces horrendous behavior when the browser is anything but max size.

Here is the code to reproduce the problem:

<!DOCTYPE html>
<html>    
<body> 

<aside></aside>

<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>
like image 663
RaminS Avatar asked Nov 27 '15 20:11

RaminS


2 Answers

Update after the question edit

The reason why your main gets pushed down under aside is because you have an invisible space, in your case the line break but can be a simple blank space as well, between your elements in your markup, which actually adds size along with the elements making it all exceed 100% width, hence create the scroll bar.

That space, which exist for inline elements (which is not positioned with absolute or fixed, or floated), needs to be taken into account together with the elements size, when calculating their width to fit their parents width (in this case the body/viewport).

Here you can read more about it and how to get rid of it or make it become 0 in size (if to keep the elements inline).

  • https://www.w3.org/TR/2010/WD-css3-text-20101005/#white-space-collapsing
  • https://css-tricks.com/fighting-the-space-between-inline-block-elements/
  • How to remove the space between inline-block elements?

Other ways to line up elements side-by-side is to use display: flex or display: table-cell, both with a similar behavior as inline elements (in the meaning of stacking horizontal in opposite to block elements, which stacks vertical), though doesn't suffer from the white space in the same way when it comes to its set size compared to actual size.

To clarify, i.e. if a width is set to 14.5% on a flex item, it takes 14.5% and no more, in opposite to an inline, which will be 14.5% plus the white space (where the white space size actually depends on the set font size)

Sample display: flex (recommended)

* {
  box-sizing: border-box;
}
html, body {
  margin: 0;
  height: 100%;
}

body {
  display: flex;
  height: 100%;
}
aside {
  width: 14%;
  margin-right: 0.5%;
  background-color: #f66;
}
main {
  width: 85.5%;  
  background-color: #66f;
}
<aside>
  aside
</aside>
<main>
  main
</main>

Sample display: table-cell (for older browsers)

* {
  box-sizing: border-box;
}
html, body {
  margin: 0;
  height: 100%;
}

body {
  display: table;
  width: 100%;
  height: 100%;
}
aside {
  display: table-cell;
  width: 14%;
  background-color: #f66;
}
main {
  display: table-cell;
  width: 85.5%;
  background-color: #66f;
}
.margin {
  display: table-cell;
  width: 0.5%;
}
<aside>
  aside
</aside>

<div class="margin"></div>

<main>
  main
</main>

Note:

Other ways to create a margin between the aside and main when using display: table, is to use cell padding, border width etc.


With your existing code, and since you don't use normal flow, absolute positioning could be an option.

<!DOCTYPE html>
<html>    
<body> 

<aside></aside>

<main></main> 

<script>
  var h = screen.height/100;
  var w = screen.width/100;

  var e = document.getElementsByTagName("aside")[0].style;
  e.position = "absolute";                                     /*  changed  */
  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.position = "absolute";                                     /*  changed  */
  e.backgroundColor = "green";
  e.width = 85.5*w + "px";
  e.height = 69*h + "px";   
  e.left = 14.5*w + "px";                                      /*  added  */
  
  e = document.getElementsByTagName("body")[0].style;
  e.margin = e.padding = "0";
  e.backgroundColor = "black";
  
</script>
</body>
</html>

Update 2

The problem with your code is it runs before the DOM is completely finished, hence create scroll bars. Try below sample, where I added a delay, and you'll see it works (when browser runs maximized).

<!DOCTYPE html>
<html>
<script>

  function runOnLoad() {

  setTimeout(function() {

  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";
  e.fontSize = "0";

  }, 200)

  }
</script>

<body onload="runOnLoad();"> 

<aside></aside>

<main></main> 

</body>
</html>
like image 150
Asons Avatar answered Oct 22 '22 15:10

Asons


The scrollbar hypothesis is right. Here is the solution: How to get screen width without (minus) scrollbar?. I tested it and using document.body.clientWidth solves the issue. The other problem is that inline-block elements need to be chained together without spaces or newlines (to avoid unwanted margins).

<body><aside></aside><main></main></body>
like image 24
JoostS Avatar answered Oct 22 '22 13:10

JoostS