Search code examples
javascriptgoogle-chromefirefoxdompixel

Fill window with divs. Div pixel height displayed incorrectly in google chrome. Width works


I want to fill the window size with divs. For a specified div size in px, the screen will be filled as much as it can be, leaving a remainder edge amount of px on the side and bottom. This remainder amount is then divided by the number of cells in the row (or column) and that is then added to the height (or width) of each cell in the row (or column).

For the width this works perfectly but when the same logic is applied to the height, it breaks. Both width and height work in firefox.

Screenshot: https://i.sstatic.net/7XWnX.png

JSfiddle of making the divs: https://jsfiddle.net/xb82c4zt/

Live: http://conwaygameoflife.heroku.com/

  var windowWidth = window.innerWidth;
  var windowHeight = window.innerHeight;
  var size = 100;
  // Calculate the number of cells we can fit in the width 
  //and height (there will be extra space)
  w = Math.floor(windowWidth / size);
  h = Math.floor(windowHeight / size);
  // Calculate the extra space
  var widthDiff = windowWidth % size;
  var heightDiff = windowHeight % size;
  // Add the needed amount of height and width to each cell to fill the window
  var widthSize = size + widthDiff / w;
  var heightSize = size + heightDiff / h;
  // Begin to alter the DOM
  var parentDiv = document.createElement('div');
  parentDiv.className = 'grid';  
  for(var y = 0; y < h; y++) {
    for(var x = 0; x < w; x++) {
      var cellDiv = document.createElement('div')
      cellDiv.className = 'cellDiv'
      cellDiv.style.height = heightSize + 'px'; 
      cellDiv.style.width = widthSize + 'px'; 
      parentDiv.appendChild(cellDiv)
    }
  }
  document.body.appendChild(parentDiv)

Solution

  • In Chrome (and probably other browsers), height and width pixel values are truncated! See this stackoverflow answer with the related jsFiddle

    Precentage values are truncated too, but not as severely. So, to solve this you can convert pixels to percentages as I did in this jsFiddle.

    The main thing I added was:

    var widthPercent = widthSize / windowWidth * 100;
    var heightPercent = heightSize / windowHeight * 100;
    

    Because we're using percentages now, the parent container must have width/height:

    parentDiv.style.height = windowHeight + 'px';
    parentDiv.style.width = windowWidth + 'px';
    

    And changed the loop to:

    for(var x = 0; x < w*h; x++) {
        var cellDiv = document.createElement('div');
        cellDiv.className = 'cellDiv';
        cellDiv.style.height = heightPercent + '%'; 
        cellDiv.style.width = widthPercent + '%'; 
        parentDiv.appendChild(cellDiv)
    }
    

    Now this doesn't always work in chrome perfectly. However, it does make it perfect in some cases... basically depends on when (and how drastic) the truncation of percentages is.


    After further reflection, it looks like percentages get resolved to fractional pixel values as well... which still get truncated in Chrome. So, let's make our math better, and figure out the biggest non-fractional pixel value we can use... it's actually really easy. See here

    Basically, we just floor the values, then center the grid so that we can make it look nice.


    edit: wasn't very happy with this answer, so screwed with it some more. Added a function that found the closest multiple of window size and made it so that it would prefer that number. Makes it work in most screen sizes, and has a fallback to the percentage method if it doesn't perfectly work. See here. However, because it relies on a recursive (naive) algorithm to find the closest multiple, it's really easy to screw your browser performance. Limiting to only 5-10 pixels of search space helps. The gist of it:

    function closestMultiple(width, size, n, limit) {
        if(n > limit) {
            return {m: width/size, s:size};
        }
        if((width % (size+n)) == 0) {
            return {m: width / (size+n), s: size+n};
        } else if((width % (size-n)) == 0) {
            return {m: width / (size-n), s: size-n};
        }
    
        return closestMultiple(width, size, n+1, limit);
    }
    

    It's very naive and ignores things like "an odd width will never be divisible by an even number"... so there's a ton of room for improvement. Check out this discussion and this discussion for more on this.