javascript进阶知识27 - client、offset与scroll与盒模型

155 阅读5分钟

offset

offsetParent

offsetParent就是查找距离该子元素最近的进行过定位的父元素(position:absolute、relative、fixed),如果其父元素中均不存在定位则offsetParent为:body元素。

我们可以对自身的定位以及父级定位分为以下几种情况:

  • 自己存在fixed定位,则其offsetParent为null;
  • 自己不存在定位,且父元素也不存在定位,offsetParent为元素
  • 元素自身无定位,且父元素存在定位,offsetParent为离自身最近且经过定位的父元素
  • 元素的`offsetParent`是null

我们来看一下例子:

1、自己存在fixed定位:

<body>
    <div id="box" style="position: fixed;"></div>
    <script>
       let box = document.querySelector('#box');
       console.log(box.offsetParent); // null
    </script>
</body>

2、自己不存在定位,且父元素也不存在定位:

<body>
    <div id="parent">
        <div id="box" ></div>
    </div>
    <script>
       let box = document.querySelector('#box');
       console.log(box.offsetParent); // <body>...</body>
    </script>
</body>

3、元素自身无定位,且父元素存在定位:

<body>
    <div id="parent" style="position: absolute;">
        <div id="box" ></div>
    </div>
    <script>
       let box = document.querySelector('#box');
       console.log(box.offsetParent); // <div id="parent" style="position: absolute;">...</div>
    </script>
</body>

4、元素的offsetParent:

<body>
    <script>
       console.log(document.body.offsetParent); // null
    </script>
</body>

offsetWidth与offsetHeight

offsetWidth = box.content.width + box.border-left.width + box.border-right.width + box.padding-left.width + box.padding-right.width;

offsetHeight = box.content.height+ box.border-top.width + box.border-bottom.width + box.padding-top.width + box.padding-bottom.width;

这是基于标准盒模型的,如果是怪异盒模型呢(IE盒模型)?

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box {
            box-sizing: border-box;
            width: 300px;
            height: 300px;
            border: 1px solid red;
            color: #fff;
            padding: 20px;
        }
    </style>
</head><body>
    <div id="parent" >
        <div id="box" ></div>
    </div>
    <script>
       let box = document.querySelector('#box');
       console.log(box.offsetWidth); //300
    </script>
</body>

我们发现怪异盒模型的offsetWidth和offsetHeight就等于其width和height。

注意:offsetWidth和offsetHeight是只读的,不能修改,如果要修改其宽高,需要使用box.style.width = xxxx+‘px’

offsetTop与offsetLeft

offsetTop:当前元素的上边框距离他的offsetParent的上边框之间的距离(注意:这不会加上offsetParent的border)

offsetLeft:当前元素的左边框距离和他的offsetParent的左边框之间的距离(注意:这不会加上offsetparent的border)

    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #parent {
            margin: 20px;
            width: 500px;
            height: 500px;
            background-color: red;
            border: 5px solid #000;
            position:relative;
        }
​
        #box {
            box-sizing: border-box;
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin-top: 30px;
            margin-left: 50px;
        }
    </style>
</head><body>
    <div id="parent" >
        <div id="box"></div>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.offsetTop);   // 30
        console.log(box.offsetLeft);  // 50
    </script>
</body>

这里的offsetTop和offsetLeft就等于其margin-top和margin-left。

如果我们当前元素有绝对定位的话:

<style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #parent {
            margin: 20px;
            width: 500px;
            height: 500px;
            background-color: red;
            border: 5px solid #000;
            position:relative;
        }
​
        #box {
            box-sizing: border-box;
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            position: absolute;
            left: 50px;
            top: 100px;
        }
    </style>
</head><body>
    <div id="parent" >
        <div id="box"></div>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.offsetTop);    // 100
        console.log(box.offsetLeft);   // 50
    </script>

这里的offsetTop和offsetLeft就等于其top和left。

如果父元素没定位:

如果上面的例子father盒子没有定位,那么box盒子的offsetParent就是body,这就可以看出其实offsetTop和offsetLeft没有加上当前元素父元素的border。

    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #parent {
            margin: 20px;
            width: 500px;
            height: 500px;
            background-color: red;
            border: 5px solid #000;
        }
​
        #box {
            /* box-sizing: border-box; */
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
        }
    </style>
</head><body>
    <div id="parent" >
        <div id="box"></div>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.offsetParent)
        console.log(box.offsetTop);  // 75
        console.log(box.offsetLeft); // 75
    </script>
</body>

我们可以看出,box的margin为50px,parent盒子的margin为20px,剩下的5px就是parent的border。

client

client 客户端大小

client有四个属性:

  • clientWidth:content + padding;
  • clientHeight:content + padding
  • clientTop:上边框大小
  • clientLeft:左边框大小

这些属性都是只可读的。

clientWidth与clientHeight

clientWidth === 内容区域宽度 + padding左右;

clientHeight === 内容区高度 + padding上下;

    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #parent {
            box-sizing: border-box;
            margin: 20px;
            width: 500px;
            height: 500px;
            background-color: red;
            border: 5px solid #000;
        }
​
        #box {
            /* box-sizing: border-box; */
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
        }
    </style>
</head><body>
    <div id="parent" >
        <div id="box"></div>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.clientWidth); // 340
        console.log(box.clientHeight) // 340
    </script>
</body>

如果是offsetWidth与offsetHeight就是345了。

clientTop与ClientLeft

代表边框的大小,而不是与谁的距离。

    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #parent {
            box-sizing: border-box;
            margin: 20px;
            width: 500px;
            height: 500px;
            background-color: red;
            border: 5px solid #000;
        }
​
        #box {
            /* box-sizing: border-box; */
            width: 300px;
            height: 300px;
            border-top: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
        }
    </style>
</head><body>
    <div id="parent" >
        <div id="box"></div>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.clientTop); // 5
        console.log(box.clientLeft) // 0
    </script>
</body>

client的作用

一般我们常常使用clientWidh与clientHeight来查看整个客户端的大小。

console.log(document.documentElement.clientWidth);
console.log(document.documentElement.clientHeight);

但是要注意的是,clientWidth这个获取到的是不加滚动条和边框的,获取到的是可见区域的宽度。

clientHeight获取到的是可见区域的高度,不是整个页面的高度。

scroll

scrollWidth和scrollHeight

scrollWidth和scrollHeight获取到的是真实宽度和真实高度;

  • scrollWidth:真实宽度,可视区的宽度+被隐藏区域的宽度
  • scrollHeight:真实高度,可视区的高度+被隐藏区域的高度
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #box {
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
            overflow: scroll;
        }
        li {
            list-style: none;
            width: 200%;
            height: 50px;
        }
    </style>
</head><body>
    <div id="box">
        <ul>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
        </ul>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.scrollWidth);  // 586
        console.log(box.scrollHeight); // 540
        
        console.log(box.clientWidth);  // 323
        console.log(box.clientHeight); // 323
        
        console.log(box.offsetWidth);  // 350
        console.log(box.offsetWidth);  // 350
    </script>
</body>

我们发现clientWidthclientHeight是不包含滚动条大小的,而offsetWidthoffsetHeight是包含滚动条大小的,scrollWidthscrollHeight是包含滚动部分的!

我们可以计算:

clientWidth==width:300 + padding:40 - 滚动条 == clientHeight

得出滚动条宽度和高度为:17

offsetWidth == width:300 + padding:40 + border:10 == offsetHeight === 350

scrollWidth中,box的宽为300,但是滚动条为17,所以真实宽度为300-17 = 283,那么li的宽度为283*2 = 586,则scrollWidth ===586。所以当有隐藏元素的时候,scrollWidth是只包括真实的(内容)宽度,不包括滚动条、padding、边框的。

如果当前元素没有隐藏元素呢?

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #box {
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
            overflow: scroll;
        }
        li {
            list-style: none;
            height: 50px;
        }
    </style>
</head><body>
    <div id="box">
        <ul>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
            <li>1</li>
        </ul>
    </div>
    <script>
        let box = document.querySelector('#box');
        console.log(box.scrollWidth);  // 323
        console.log(box.scrollHeight); // 540
        
        console.log(box.clientWidth);  // 323
        console.log(box.clientHeight); // 323
        
        console.log(box.offsetWidth);  // 350
        console.log(box.offsetWidth);  // 350
    </script>
</body>

那么scrollWidth就等于clientWidth.

scrollTop和scrollLeft

scrollTop指的是当前元素被卷去的高度,也就是内容区顶端到可见区顶端的距离;scrollLeft值的是当前元素被卷去的宽度,也就是内容区左端到可视区左端的距离。

注意:scrollTop和scrollLeft是可写可读的,也就是可以修改这个属性值。

css盒模型

CSS盒模型本质是一个盒子,由边距、边框、填充和实际内容组成。我们知道css的盒模型分为两种:标准盒模型、怪异盒模型(IE盒模型),他们又是咋计算的呢?

  • 标准盒模型:
    • 标准盒模型的实际宽度 === 内容区宽度 + padding + border + margin;
    • 标准盒模型的width === 内容区宽度(我们设置的width);
    • 增加内边距、边框和外边距不会影响内容区域的尺寸,但是会增加元素框的总尺寸,也就是说标准盒模型的padding和border会撑大盒子。
  • 怪异盒模型:
    • 怪异盒模型的实际宽度 === 我们设置的宽度(width) + marin;
    • 怪异盒模型的width === 内容区宽度 + padding + border;
    • 内容区宽度 === 我们设置的宽度 - padding - border;
    • 设置 padding 、border 后,真正宽度或高度就会改变。
  • 属性值:border-box(IE盒模型)、content-box(标准盒模型)

注意: margin不会对盒子的width和height起作用,它只是会影响盒子的实际位置。

标准盒模型:

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #box {
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
        }
    </style>
</head><body>
    <div id="box">
       111
    </div>
</body>

image.png

把它设置为怪异盒模型后:

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
​
        #box {
            box-sizing: border-box;
            width: 300px;
            height: 300px;
            border: 5px solid green;
            padding: 20px;
            background-color: #fff;
            margin: 50px;
        }
    </style>
</head><body>
    <div id="box">
       111
    </div>
</body>

image.png

总结:

  • client:
    • clientWidth和clientHeight等于:width/height + padding;
    • clientTop和clientLeft:border-top和border-left;
  • offset:
    • offsetWidth和offsetHeight等于:width/height + padding + border;
    • offsetLeft和offsetTop:本元素的顶部或者左边距离offsetParent的顶部或者左边(不加border)的距离;
  • scroll:
    • scrollWidth和scrollHeight:
      • 没有隐藏部分:等于clientWidth/clientHeight;
      • 有隐藏部分:等于实际宽度/高度 + 隐藏宽度/高度;
    • scrollTop和scrollLeft:被卷去的高度或者宽度;