Understand the element offset (offsetLeft,offsetTop,offsetWidth,offsetHeight) in js

offset dimension

Offset: includes all visible space occupied by the element on the screen. The visible size of the element is determined by its height and width, including all inner margins, scroll bar and border sizes (note that the outer margins are not included).

The following four attributes can get the offset of an element

  1. offsetHeight: the amount of space occupied by the element in the vertical direction, in pixels. Including the height of the element (visible), the height of the horizontal scroll bar, the height of the upper border and the height of the lower border.

  2. offsetWidth: the amount of space occupied by the element in the horizontal direction, in pixels. It includes the width of the element (visible), the width of the vertical scroll bar, the width of the left border and the width of the right border.

3: offsetLeft: the pixel distance from the left outer border of the element to the left inner border of the containing element.

4: offsetTop: the pixel distance from the upper outer border of the element to the upper inner border of the containing element.

The offsetLeft and offsetTop attributes are related to the containing element, and the reference of the containing element is saved in offsetParent. Please note that the values of offsetParent and parentNode are not necessarily equal.

With a theoretical basis, we should start to see if the facts are really like that:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .parent {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            background-color: red;
            border: 10px solid #000;
            overflow: hidden;
        }

        .child {
            width: 300px;
            height: 300px;
            border: 1px solid #000;
            padding: 10px;
            margin: 50px 90px;
            background-color: green;
        }
    </style>
</head>

<body>
    <div class="parent">
        <div class="child"></div>
    </div>
    <script>
        var child = document.querySelector('.child');
        var html = '';
        html += "offsetWidth=" + child.offsetWidth + "<br>";
        html += "offsetHeight=" + child.offsetHeight + "<br>";
        html += "offsetTop=" + child.offsetTop + "<br>";
        html += "offsetLeft=" + child.offsetLeft;
        child.innerHTML = html;
    </script>
</body>

</html>

Before looking at the results, we guess what will happen below according to our understanding of the theory.

The guess results of the related offset with the class name child are as follows: (guess the premise that child is included in the parent)
Offsetwidth = 102 (left and right inner margins) + 12 (left and right borders) + 300 (width of element) + 0 (height of vertical scroll bar) = 322;

offsetHeight = 102 (upper and lower inner margins) + 12 (upper and lower borders) + 300 (height of elements) + 0 (height of horizontal scroll bar) = 322;

offsetLeft = 90 (pixel distance from the upper outer border of the element to the upper inner border containing the element, here is the distance from the left outer edge);

offsetTop = 50 (pixel distance from the upper outer border of the element to the upper inner border containing the element, here is the distance from the upper outer edge);

Let's look at the actual results:

It is not difficult to find that offsetWidth and offsetHeight are consistent with our guess, but there are large deviations in the other two attributes for the following reasons:

offsetParent: refers to the nearest relative (absolute) ancestor element of the element. If no ancestor element is located, it will point to the body element.

Now modify the parent style as follows:

.parent {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            background-color: red;
            border: 10px solid #000;
            overflow: hidden;
            position: relative;
        }

Review the results again:

Now the result is consistent with our guess. Therefore, when using offsetLeft and offsetTop, you must pay attention to the direction of offsetParent.

Next, let's do the following tests: (for test convenience, specify the offsetParent of the child as the parent)

1. How does the box model of an element affect the above four attributes?

Modify the css code of child as follows:

.child {
            width: 300px;
            height: 300px;
            border: 1px solid #000;
            padding: 10px;
            margin: 50px 90px;
            background-color: green;
            box-sizing: border-box;
        }

View results:

It is found that changing the box model will affect the offset width and offset height of the element

2. How does element positioning affect the above four attributes?

Modify the css code as follows:

.parent {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            background-color: red;
            border: 10px solid #000;
            overflow: hidden;
            position: relative;
        }

        .child {
            position: absolute;
            top: 30px;
            left: 50px;
            width: 300px;
            height: 300px;
            border: 1px solid #000;
            padding: 10px;
            margin: 50px 90px;
            background-color: green;
            box-sizing: border-box;;
        }

View results:

It is found that there are 30 more offsetTop (30 is the azimuth value in the top direction of the child's absolute positioning relative to the parent) and 50 more offsetLeft (50 is the azimuth value in the left direction of the child's absolute positioning relative to the parent).

The offsetTop and offsetLeft in the offset of the element can be related to the outer margin in the offsetParent and the orientation value of the positioning.

Now the offsetLeft and offsetTop of the calculation element on the page are given (note that it is not just the value in the included element)

var getOffset = {
            left: function (element) {
                var actualLeft = element.offsetLeft;
                var current = element.offsetParent;
                while (current) {
                    actualLeft += current.offsetLeft;
                    current = current.offsetParent;
                }
                return actualLeft;
            },
            top: function (element) {
                var actualTop = element.offsetTop;
                var current = element.offsetParent;
                while (current) {
                    actualTop += current.offsetTop;
                    current = current.offsetParent;
                }
                return actualTop;
            }
        }

Here, we recursively find the offsetParent of the element, and then add the offsetTop or offsetLeft in the offsetParent all the time to get the offsetLeft and offsetTop of the element in the page. At first glance, it seems perfect, but two functions can only get a basically accurate value. Why do you say so? You can get the answer through the following cases.

Test case 1 code is as follows:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .parent {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            background-color: red;
            border: 10px solid #000;
            overflow: hidden;
        }

        .child {
            width: 300px;
            height: 300px;
            border: 10px solid #000;
            padding: 10px;
            margin: 50px 90px;
            background-color: green;
        }
    </style>
</head>

<body>
    <div class="parent">
        <div class="child"></div>
    </div>
    <script>
        var child = document.querySelector('.child');
        var html = '';
        html += "offsetWidth=" + child.offsetWidth + "<br>";
        html += "offsetHeight=" + child.offsetHeight + "<br>";
        html += "offsetTop=" + child.offsetTop + "<br>";
        html += "offsetLeft=" + child.offsetLeft;
        child.innerHTML = html;
    </script>
</body>

</html>

Here, the offsetParent of child is the body element, and this result is the accurate result of the offset of the element on the page.

View results:

Please remember this result. Next, we use the defined method to obtain the offsetTop and offsetLeft of the child.

The code of test case 2 is as follows:

<script>
        var child = document.querySelector('.child');
        var html = '';
        var getOffset = {
             left: function(element){
                 var actualLeft = element.offsetLeft;
                 var current = element.offsetParent;
                 while(current) {
                     actualLeft += current.offsetLeft;
                     current = current.offsetParent;
                 }
                 return actualLeft;
             },
             top: function(element){
                var actualTop = element.offsetTop;
                 var current = element.offsetParent;
                 while(current) {
                     actualTop += current.offsetTop;
                     current = current.offsetParent;
                 }
                 return actualTop;
             }
         }
        html += "offsetWidth=" + child.offsetWidth + "<br>";
        html += "offsetHeight=" + child.offsetHeight + "<br>";
        html += "offsetTop=" + getOffset.top(child) + "<br>";
        html += "offsetLeft=" + getOffset.left(child);
        child.innerHTML = html;

        
    </script>
</body>

Here, the offsetLeft and offsetTop of the child element are not directly used, but calculated through the defined function.

The results are as follows:

The result is the same. Does it mean that the function calculates the accurate result?, Keep looking down.

Test case 3 code is as follows:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .parent {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            background-color: red;
            border: 10px solid #000;
            overflow: hidden;
            /*Note that this is equivalent to setting the child's offsetParent to parent*/
            position: relative;
        }

        .child {
            width: 300px;
            height: 300px;
            border: 10px solid #000;
            padding: 10px;
            margin: 50px 90px;
            background-color: green;
        }
    </style>
</head>

<body>
    <div class="parent">
        <div class="child"></div>
    </div>
    <script>
        var child = document.querySelector('.child');
        var html = '';
        var getOffset = {
            left: function (element) {
                var actualLeft = element.offsetLeft;
                var current = element.offsetParent;
                while (current) {
                    actualLeft += current.offsetLeft;
                    current = current.offsetParent;
                }
                return actualLeft;
            },
            top: function (element) {
                var actualTop = element.offsetTop;
                var current = element.offsetParent;
                while (current) {
                    actualTop += current.offsetTop;
                    current = current.offsetParent;
                }
                return actualTop;
            }
        }
        html += "offsetWidth=" + child.offsetWidth + "<br>";
        html += "offsetHeight=" + child.offsetHeight + "<br>";
        html += "offsetTop=" + getOffset.top(child) + "<br>";
        html += "offsetLeft=" + getOffset.left(child);
        child.innerHTML = html;


    </script>
</body>


</html>

Notice the part of the parent style annotation, and now look at the results:

Find out if the offsetLeft and offsetTop on the page are 10 less than the exact results. Why is this phenomenon? The answer can be obtained from the definitions of offsetLeft and offsetTop

offsetLeft: the pixel distance from the left outer border of the element to the left inner border of the containing element.

offsetTop: the pixel distance from the upper outer border of the element to the upper inner border of the containing element.

In this example, we set the child's offsetParent to the parent. According to the definition, we can get that the child's offsetTop in the parent is 50 and the parent's offsetTop in the body is 100 (offsetTop: the pixel distance between the upper outer border of the element and the upper inner border of the element). Therefore, we ignore the parent's border height (10px) in the calculation Therefore, there is an error of 10px, and the explanation of offset left's 10 error is the same.

Let's look at a case:

Test case 4 code is as follows:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .parent {
            width: 500px;
            height: 500px;
            margin: 100px auto;
            background-color: red;
            border: 10px solid #000;
            overflow: hidden;
            /*Pay attention here*/
            box-sizing: border-box;
        }

        .child {
            width: 300px;
            height: 300px;
            border: 10px solid #000;
            padding: 10px;
            margin: 50px 90px;
            background-color: green;
        }
    </style>
</head>

<body>
    <div class="parent">
        <div class="child"></div>
    </div>
    <script>
        var child = document.querySelector('.child');
        var html = '';
        var getOffset = {
            left: function (element) {
                var actualLeft = element.offsetLeft;
                var current = element.offsetParent;
                while (current) {
                    actualLeft += current.offsetLeft;
                    current = current.offsetParent;
                }
                return actualLeft;
            },
            top: function (element) {
                var actualTop = element.offsetTop;
                var current = element.offsetParent;
                while (current) {
                    actualTop += current.offsetTop;
                    current = current.offsetParent;
                }
                return actualTop;
            }
        }
        html += "offsetWidth=" + child.offsetWidth + "<br>";
        html += "offsetHeight=" + child.offsetHeight + "<br>";
        html += "offsetTop=" + getOffset.top(child) + "<br>";
        html += "offsetLeft=" + getOffset.left(child);
        child.innerHTML = html;


    </script>
</body>


</html>

Pay attention to the parent style annotation.

The results are as follows:

It is found that the offset left of child on the page is 10 more than the standard result. Because the box model of the parent is changed, the original expanded border of the parent becomes filled inward, and its border length is 10, so the offset left result is 10 more.

It can be found that there are certain errors in the offsetLeft and offsetTop of the above calculation elements on the page. The relevant factors are as follows:

  1. Modify the offsetParent of the element
  2. Modify the box model of offsetParent (if offsetParent sets the border and inner margin)

Note: the offsets of elements are read-only and need to be recalculated every time they are accessed. If these attribute values are to be used repeatedly, please save them in local variables to improve performance

Therefore, in our actual development, how to write code to minimize the error is worth thinking about. Of course, other tests on the offset are not all carried out. If there is an error, please let me know and I will modify it in time.

Tags: Javascript css3 css

Posted on Mon, 01 Nov 2021 10:00:06 -0400 by manchuwok