Blog-Archiv

Montag, 11. April 2016

JS Get / Set Element Width / Height

This is another Blog about HTML Element Dimensions. It contains JavaScript (JS) functions to calculate the outer and inner width of elements, and to set these dimensions accordingly. I will cover block and inline-block elements (but not inline elements), and the infamous box-sizing CSS property.

Mind that my usage of the terms outer and inner for width and height differs from the jQuery meanings for outer and inner. While jQuery's .outerWidth() delivers element.offsetWidth (optionally including margin), and .innerWidth() delivers element.clientWidth, my understanding of "inner" is the usable element space within, i.e. the inner rectangle without padding, and with "outer" I describe the space an element would take as a whole, i.e. including margin.

The reason why I won't cover inline elements is that these can not be resized by setting CSS width and height. If you want to size a SPAN, you need to set it display: inline-block, or its position: fixed or absolute. Further inline elements do not include top and bottom margins into their height (will be painted over other elements when used), while they do include left and right margins into their width.

Demo

Here is some test HTML. Four different DIV elements are arranged vertically, the first two have box-sizing: content-box, the second two box-sizing: border-box. The third element has display: inline-block, all others are display: block (DIV default). All four elements have different margins, borders and paddings.

It is easy to visualize a border, you just need to give it a width and a colour. To visualize margins and paddings is not so easy. Thus I decided to enclose each demo element into a DIV wrapper that has display: inline-block (does not expand to whole width) and a dotted black border. This will visualize the margin as the space between the coloured and the dotted black border. To visualize the padding, I nested a DIV element into the demo DIV, which has 100% height to always fill up its parent entirely, and a yellow background colour. Thus it renders the padding as the space between the yellow area and the coloured border.

The challenge now is to set all these demo elements to same size. As I talked about inner and outer size, both can be done here:

  • when you trigger "Set Inner Size", all yellow rectangles should have the same dimension
  • when you trigger "Set Outer Size", all black dotted borders should have the same dimension

Pixels


DIV 1

DIV 2

DIV 3

DIV 4

Right-click on an element and trigger "Inspect Element" to see what your browser debugger displays as "Box Model". You need to focus the element with CSS class sameSize, not its parent or child (which are just for visualizing margin and padding).


Demo HTML and CSS Source

Click on left arrow to see the demo HTML and CSS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>JS Read and Write HTML Element Width and Height</title>
    
    <style type="text/css">
      .outer {
        border: 1px dotted black; /* this border makes margin of nested DIV visible */
        margin: 4px; /* keep a little distance to other outer DIVs */
        display: inline-block; /* wrap around the element to show margin, no width 100% */
        text-align: initial; /* reset center alignment */
      }
      .inner {
        background-color: yellow; /* color makes parent padding visible */
        height: 100%; /* spread background-color to bottom */
        font-style: "sans-serif";
        font-size: 75%;
      }
      
      #one {
        border: 10px solid LightGreen;
        padding: 10px;
        margin: 10px;
      }
      #two {
        border: 20px solid orange;
        padding: 20px;
        margin: 20px;
      }
      #three {
        border: 30px solid cyan;
        padding: 30px;
        margin: 30px;
        box-sizing: border-box;
        display: inline-block;
      }
      #four {
        border: 40px solid magenta;
        padding: 40px;
        margin: 40px;
        box-sizing: border-box;
      }
    </style>
    
  </head>
     
  <body>

    <div style="text-align: center;">
  
      <div class="outer">
        <div id="one" class="sameSize">
          <div class="inner">DIV 1</div>
        </div>
      </div>
    
      <br>
        
      <div class="outer">
        <div id="two" class="sameSize">
          <div class="inner">DIV 2</div>
        </div>
      </div>
        
      <br>
        
      <div class="outer">
        <div id="three" class="sameSize">
          <div class="inner">DIV 3</div>
        </div>
      </div>
        
      <br>
        
      <div class="outer">
        <div id="four" class="sameSize">
          <div class="inner">DIV 4</div>
        </div>
      </div>
        
    </div>
        
  </body>
</html>

JS Source

Here comes the JS source code working in this demo. First the module frame:

    var elementDimensions = (function()
    {
      "use strict";
      
      var self = {};

      .....

      return self;
      
    })();

The page-gobal variable elementDimensions evaluates to a singleton object created from an immediately invoked function expression (IIFE). A function that creates an object (or a function) containing inner functions is called "module" in JS.

All following JS source goes to where the "...." is.

Reading Dimensions

      self.getInnerSize = function(element) {
        return {
          width:  self.getInnerWidth(element),
          height: self.getInnerHeight(element)
        };
      };
      
      self.getInnerWidth = function(element) {
        var style = window.getComputedStyle(element);
        var borderWidth = window.parseInt(style["border-left-width"]) + window.parseInt(style["border-right-width"]);
        var paddingWidth = window.parseInt(style["padding-left"]) + window.parseInt(style["padding-right"]);
        return element.offsetWidth - borderWidth - paddingWidth;
      };
      
      self.getInnerHeight = function(element) {
        var style = window.getComputedStyle(element);
        var borderHeight = window.parseInt(style["border-top-width"]) + window.parseInt(style["border-bottom-width"]);
        var paddingHeight = window.parseInt(style["padding-top"]) + window.parseInt(style["padding-bottom"]);
        return element.offsetHeight - borderHeight - paddingHeight;
      };
      
      
      
      self.getOuterSize = function(element) {
        return {
          width:  self.getOuterWidth(element),
          height: self.getOuterHeight(element)
        };
      };
      
      self.getOuterWidth = function(element) {
        var style = window.getComputedStyle(element);
        var marginWidth = window.parseInt(style["margin-left"]) + window.parseInt(style["margin-right"]);
        return element.offsetWidth + marginWidth;
      };
      
      self.getOuterHeight = function(element) {
        var style = window.getComputedStyle(element);
        var isInlineElement = (style.display === "inline");
        var marginHeight = isInlineElement ? 0 : window.parseInt(style["margin-top"]) + window.parseInt(style["margin-bottom"]);
        return element.offsetHeight + marginHeight;
      };

For the inner width and height we can ignore box-sizing, it will be the same for content-box and border-box. Simply subtract the padding and border from offsetWidth. This would also work for inline elements. Although they do not have a clientWidth, their offsetWidth should always be present when they are visible.

The same applies to outer width and height. We simply add margins to offsetWidth. Just when it is an inline element, we must not add them when calculating the height the element claims.

Writing Dimensions

      self.setInnerSize = function(element, innerWidth, innerHeight) {
         self.setInnerWidth(element, innerWidth);
         self.setInnerHeight(element, innerHeight);
      };
      
      self.setInnerWidth = function(element, innerWidth) {
        var style = window.getComputedStyle(element);
        var cssWidth = innerWidth;
        
        if (style["box-sizing"] === "border-box") {
          var borderWidth = window.parseInt(style["border-left-width"]) + window.parseInt(style["border-right-width"]);
          cssWidth += borderWidth;
          var paddingWidth = window.parseInt(style["padding-left"]) + window.parseInt(style["padding-right"]);
          cssWidth += paddingWidth;
        }
        
        if (cssWidth > 0)
          element.style["width"] = cssWidth+"px";
      };
      
      self.setInnerHeight = function(element, innerHeight) {
        var style = window.getComputedStyle(element);
        var cssHeight = innerHeight;
        
        if (style["box-sizing"] === "border-box") {
          var borderHeight = window.parseInt(style["border-top-width"]) + window.parseInt(style["border-bottom-width"]);
          cssHeight += borderHeight;
          var paddingHeight = window.parseInt(style["padding-top"]) + window.parseInt(style["padding-bottom"]);
          cssHeight += paddingHeight;
        }
        
        if (cssHeight > 0)
          element.style["height"] = cssHeight+"px";
      };
      
      
      
      self.setOuterSize = function(element, outerWidth, outerHeight) {
        self.setOuterWidth(element, outerWidth);
        self.setOuterHeight(element, outerHeight);
      };
      
      self.setOuterWidth = function(element, outerWidth) {
        var style = window.getComputedStyle(element);
        var marginWidth = window.parseInt(style["margin-left"]) + window.parseInt(style["margin-right"]);
        var cssWidth = outerWidth - marginWidth;
        
        if (style["box-sizing"] !== "border-box") {
          var borderWidth = window.parseInt(style["border-left-width"]) + window.parseInt(style["border-right-width"]);
          cssWidth -= borderWidth;
          var paddingWidth = window.parseInt(style["padding-left"]) + window.parseInt(style["padding-right"]);
          cssWidth -= paddingWidth;
        }
        
        if (cssWidth > 0)
          element.style["width"] = cssWidth+"px";
      };
      
      self.setOuterHeight = function(element, outerHeight) {
        var style = window.getComputedStyle(element);
        var marginHeight = window.parseInt(style["margin-top"]) + window.parseInt(style["margin-bottom"]);
        var cssHeight = outerHeight - marginHeight;
        
        if (style["box-sizing"] !== "border-box") {
          var borderHeight = window.parseInt(style["border-top-width"]) + window.parseInt(style["border-bottom-width"]);
          cssHeight -= borderHeight;
          var paddingHeight = window.parseInt(style["padding-top"]) + window.parseInt(style["padding-bottom"]);
          cssHeight -= paddingHeight;
        }
        
        if (cssHeight > 0)
          element.style["height"] = cssHeight+"px";
      };

Writing dimensions is more complex. We need to consider the box-sizing CSS property. When we want to set the inner width of an element with box-sizing: border-box, we need to add its border and padding to the given size.

Symmetrically we need to subtract the border and padding from given size when setting the outer width of an element with box-sizing: content-box.

Remember:

  • The CSS width on box-sizing: border-box is the same as offsetWidth, but for box-sizing: content-box (default), offsetWidth includes border and padding. Margins are never included, neither in clientWidth nor offsetWidth.

For the current state of this utility go to my homepage.




Keine Kommentare: