Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is measured width of divs different from browser measurement?

Tags:

I have a small tool to try several flexbox parameters. It can add and remove divs from a flexbox container and then shows the width of these added elements using jQuery. But the measured width is different from what I measure in the browser inspector.

Using the "plus" and "minus" buttons, elements can be added. The width of the first element should be correct, but the later elements are wrongly measured. What is going on here?

function getWidth() {
  // true: include margins
  var v = parseFloat($(this).outerWidth(true)).toFixed(2);
  var span = '<span>Width: ' + v + '</span>';
  
  console.log(v);
  $(this).html(span);
}

$(document).ready(function() {
  // Initialize.
  var items = 3;
  var itemsDiv = '<div class="item">';
  var HTML = '';
  for (var i = 0; i < items; i++) {
    HTML += itemsDiv + 'Item ' + i + '</div>';
  }
  $('.container').html(HTML)
  var adjustment;

  // Add / remove items
  // Always keep at least one flex item.
  $('input[name="build"]').click(function() {
    adjustment = $(this).val();
    if (adjustment == "plus") {
      items += 1;
    }
    if (adjustment == "minus") {
      if (items > 1) {
        items -= 1;
      }
    }
    HTML = '';
    for (var i = 0; i < items; i++) {
      HTML += itemsDiv + 'Item ' + i + '</div>';
    }
    $('.container').html(HTML);
    $('.item').each(getWidth);
  });

  // Flex direction
  // Select flex-direction radio buttons and get checked value.
  $('input[name="flex-direction"]').click(function() {
    var flexDirection = $('input[name="flex-direction"]:checked').val();
    $('.container').css('flex-direction', flexDirection);
  });

  // Flex wrap
  $('input[name="flex-wrap"]').click(function() {
    var flexWrap = $('input[name="flex-wrap"]:checked').val();
    $('.container').css('flex-wrap', flexWrap);
  })
})
body {
  box-sizing: border-box;
}

.container {
  display: flex;
  flex-direction: row;
  height: 200px;
  width: 400px;
  border: 1px solid grey;
  margin: auto;
}

div.item {
  padding: 0;
  /* max-width: 80px; */
  height: 100px;
  margin: auto;
  border: 1px solid black;
}
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="./flexbox.css">
  <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
  <script src="flexbox.js"></script>
</head>
<body>
  <div class="base">
    <input type="button" name="build" value="plus" id="plus">
    <input type="button" name="build" value="minus" id="minus">
    <div class="container"></div>
    Flex Direction
    <input type="radio" name="flex-direction" id="row" value="row" checked>
    <label for="row">Row</label>
    <input type="radio" name="flex-direction" id="column" value="column">
    <label for="column">Column</label>
    <input type="radio" name="flex-direction" id="row-reverse" value="row-reverse">
    <label for="row-reverse">Row-Reverse</label>
    <input type="radio" name="flex-direction" id="column-reverse" value="column-reverse">
    <label for="column-reverse">Column-Reverse</label>
    <br />
    <input type="radio" name="flex-wrap" id="nowrap" value="nowrap" checked>
    <label for="nowrap">No Wrap</label>
    <input type="radio" name="flex-wrap" id="wrap" value="wrap">
    <label for="wrap">Wrap</label>
    <input type="radio" name="flex-wrap" id="wrap-reverse" value="wrap-reverse">
    <label for="wrap-reverse">Wrap-Reverse</label>
  </div>
</body>
</html>
like image 294
TMOTTM Avatar asked Jul 24 '18 07:07

TMOTTM


1 Answers

The calculation is fine and the difference is due to the fact that you are calculating the width of the 1st one then you add a content inside it which will affect the other. Then you do the same with the second one, etc.

At the end all of them will have the same outerWidth1 visually because they have the same content BUT while running the JS it's not the case and you cannot notice this.

Here is the code without adding content to your elements and you can clearly see that all the log give the same result simply because the HTML is not changing during the width calculation.

function getWidth() {
  // true: include margins
  var v = parseFloat($(this).outerWidth(true)).toFixed(2);
  var span = '<span>Width: ' + v + '</span>';
  
  console.log(v);
  //$(this).html(span);
}

$(document).ready(function() {
  // Initialize.
  var items = 3;
  var itemsDiv = '<div class="item">';
  var HTML = '';
  for (var i = 0; i < items; i++) {
    HTML += itemsDiv + 'Item ' + i + '</div>';
  }
  $('.container').html(HTML)
  var adjustment;

  // Add / remove items
  // Always keep at least one flex item.
  $('input[name="build"]').click(function() {
    adjustment = $(this).val();
    if (adjustment == "plus") {
      items += 1;
    }
    if (adjustment == "minus") {
      if (items > 1) {
        items -= 1;
      }
    }
    HTML = '';
    for (var i = 0; i < items; i++) {
      HTML += itemsDiv + 'Item ' + i + '</div>';
    }
    $('.container').html(HTML);
    $('.item').each(getWidth);
  });

  // Flex direction
  // Select flex-direction radio buttons and get checked value.
  $('input[name="flex-direction"]').click(function() {
    var flexDirection = $('input[name="flex-direction"]:checked').val();
    $('.container').css('flex-direction', flexDirection);
  });

  // Flex wrap
  $('input[name="flex-wrap"]').click(function() {
    var flexWrap = $('input[name="flex-wrap"]:checked').val();
    $('.container').css('flex-wrap', flexWrap);
  })
})
body {
  box-sizing: border-box;
}

.container {
  display: flex;
  flex-direction: row;
  height: 200px;
  width: 400px;
  border: 1px solid grey;
  margin: auto;
}

div.item {
  padding: 0;
  /* max-width: 80px; */
  height: 100px;
  margin: auto;
  border: 1px solid black;
}
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="./flexbox.css">
  <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
  <script src="flexbox.js"></script>
</head>
<body>
  <div class="base">
    <input type="button" name="build" value="plus" id="plus">
    <input type="button" name="build" value="minus" id="minus">
    <div class="container"></div>
    Flex Direction
    <input type="radio" name="flex-direction" id="row" value="row" checked>
    <label for="row">Row</label>
    <input type="radio" name="flex-direction" id="column" value="column">
    <label for="column">Column</label>
    <input type="radio" name="flex-direction" id="row-reverse" value="row-reverse">
    <label for="row-reverse">Row-Reverse</label>
    <input type="radio" name="flex-direction" id="column-reverse" value="column-reverse">
    <label for="column-reverse">Column-Reverse</label>
    <br />
    <input type="radio" name="flex-wrap" id="nowrap" value="nowrap" checked>
    <label for="nowrap">No Wrap</label>
    <input type="radio" name="flex-wrap" id="wrap" value="wrap">
    <label for="wrap">Wrap</label>
    <input type="radio" name="flex-wrap" id="wrap-reverse" value="wrap-reverse">
    <label for="wrap-reverse">Wrap-Reverse</label>
  </div>
</body>
</html>

Now, here is the state when you only add the content inside the first element AFTER calculating its width. We can clearly see that we don't have the same structure and not the same margin between elements thus the outerWidth of the second element will be different.

body {
  box-sizing: border-box;
}

.container {
  display: flex;
  flex-direction: row;
  height: 200px;
  width: 400px;
  border: 1px solid grey;
  margin: auto;
}

div.item {
  padding: 0;
  /* max-width: 80px; */
  height: 100px;
  margin: auto;
  border: 1px solid black;
}
All the elements have the same outerwidth here
<div class="container">
  <div class="item">Item 0</div>
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
</div>
We add the width calculation inside the first one and the margin will be reduced so the next one will be different
<div class="container">
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
</div>

Here is the next states and you can clearly see the effect.

The values used are simple placeholder

body {
  box-sizing: border-box;
}

.container {
  display: flex;
  flex-direction: row;
  height: 200px;
  width: 400px;
  border: 1px solid grey;
  margin: auto;
}

div.item {
  padding: 0;
  /* max-width: 80px; */
  height: 100px;
  margin: auto;
  border: 1px solid black;
}
<div class="container">
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
</div>
<div class="container">
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item">Item 3</div>
</div>
<div class="container">
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item"><span>Width: 100.66</span></div>
  <div class="item"><span>Width: 100.66</span></div>
</div>

In the situation with 4 elements only the margin is affected, but when it comes to more elements the inner width may also get affected due to the shrink effect:

body {
  box-sizing: border-box;
}

.container {
  display: flex;
  flex-direction: row;
  height: 200px;
  width: 400px;
  border: 1px solid grey;
  margin: auto;
}

div.item {
  padding: 0;
  /* max-width: 80px; */
  height: 100px;
  margin: auto;
  border: 1px solid black;
}
<div class="container">
  <div class="item">Item 0</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
</div>
<div class="container">
  <div class="item">Width 100</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
  <div class="item">Item 1</div>
</div>

If you want an accurate calculation, you should avoid changing the HTML content and the DOM structure while doing the calculation. You can update it before and after but not in the middle of the calculation by adding content.


1 : it's the width including margin since you are adding true to the jQuery function.


Based on a comment, here is a sample of your original code, where the span with the width info gets appended, and positioned absolute at the bottom. With this it won't affect the calculated values.

Stack snippet

function getWidth() {
  // true: include margins
  var v = parseFloat($(this).outerWidth(true)).toFixed(2);
  var span = '<span>Width: ' + v + '</span>';
  
  //console.log(v);
  $(this).append(span);
}

$(document).ready(function() {
  // Initialize.
  var items = 3;
  var itemsDiv = '<div class="item">';
  var HTML = '';
  for (var i = 0; i < items; i++) {
    HTML += itemsDiv + 'Item ' + i + '</div>';
  }
  $('.container').html(HTML)
  var adjustment;

  // Add / remove items
  // Always keep at least one flex item.
  $('input[name="build"]').click(function() {
    adjustment = $(this).val();
    if (adjustment == "plus") {
      items += 1;
    }
    if (adjustment == "minus") {
      if (items > 1) {
        items -= 1;
      }
    }
    HTML = '';
    for (var i = 0; i < items; i++) {
      HTML += itemsDiv + 'Item ' + i + '</div>';
    }
    $('.container').html(HTML);
    $('.item').each(getWidth);
  });

  // Flex direction
  // Select flex-direction radio buttons and get checked value.
  $('input[name="flex-direction"]').click(function() {
    var flexDirection = $('input[name="flex-direction"]:checked').val();
    $('.container').css('flex-direction', flexDirection);
  });

  // Flex wrap
  $('input[name="flex-wrap"]').click(function() {
    var flexWrap = $('input[name="flex-wrap"]:checked').val();
    $('.container').css('flex-wrap', flexWrap);
  })
})
body {
  box-sizing: border-box;
}

.container {
  display: flex;
  flex-direction: row;
  height: 200px;
  width: 400px;
  border: 1px solid grey;
  margin: auto;
}

div.item {
  position: relative;
  padding: 0;
  /* max-width: 80px; */
  height: 100px;
  margin: auto;
  border: 1px solid black;
}

div.item span {
  position: absolute;
  left: 0; bottom: 0;
}
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="./flexbox.css">
  <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
  <script src="flexbox.js"></script>
</head>
<body>
  <div class="base">
    <input type="button" name="build" value="plus" id="plus">
    <input type="button" name="build" value="minus" id="minus">
    <div class="container"></div>
    Flex Direction
    <input type="radio" name="flex-direction" id="row" value="row" checked>
    <label for="row">Row</label>
    <input type="radio" name="flex-direction" id="column" value="column">
    <label for="column">Column</label>
    <input type="radio" name="flex-direction" id="row-reverse" value="row-reverse">
    <label for="row-reverse">Row-Reverse</label>
    <input type="radio" name="flex-direction" id="column-reverse" value="column-reverse">
    <label for="column-reverse">Column-Reverse</label>
    <br />
    <input type="radio" name="flex-wrap" id="nowrap" value="nowrap" checked>
    <label for="nowrap">No Wrap</label>
    <input type="radio" name="flex-wrap" id="wrap" value="wrap">
    <label for="wrap">Wrap</label>
    <input type="radio" name="flex-wrap" id="wrap-reverse" value="wrap-reverse">
    <label for="wrap-reverse">Wrap-Reverse</label>
  </div>
</body>
</html>
like image 198
Temani Afif Avatar answered Sep 28 '22 18:09

Temani Afif