CSS 盒子模型和定位






4.94/5 (13投票s)
本文介绍了 CSS 盒子模型和定位的基本信息。

引言
CSS 盒子模型和 CSS 定位是网页设计中的两个基本概念。理解这些概念对于掌握页面布局和其他网页设计问题至关重要。
在本文中,我将介绍一个演示网页(可以通过 这里 运行),其中包含一些 JavaScript 代码,以帮助理解 W3C 盒子模型和 CSS 定位。该演示在最新版本的 FireFox 和 Chrome 中运行正常。
在我于 KFUPM(我任教的学校)的一个 Web 工程与设计课程中讲授这些概念时,我感到需要一些交互式过程来帮助理解这些概念,这就是这个想法的由来。
本文遵循同样的精神,提供了讲解和一些有用的示例。
文章分为三个部分:
- 演示工作原理
- CSS 盒子模型
- CSS 定位
演示工作原理
以下是演示页面的源 HTML 代码:
<html>
<head>
<title>CSS BOx Model</title>
<script type="text/javascript" >
window.onload = function () { getStyleInfo(); placeShadowDiv(); };
lastX=0; lastY=0;
function getStyleInfo()
{
var elem = document.getElementById("B");
//alert(elem);
var styleInfo = window.getComputedStyle(elem);
outstr = " width:"+styleInfo.width;
outstr += "; height:"+styleInfo.height;
outstr += "; left:"+styleInfo.left;
outstr += "; top:"+styleInfo.top;
outstr += "; margin:"+styleInfo.marginLeft; // margin is "" for FireFox
outstr += "; padding:"+styleInfo.paddingLeft; // padding is "" for FireFox
outstr += "; border:"+styleInfo.borderLeftWidth;
//document.write(elem.currentStyle);
//alert(document.getElementById("styleInfo"));
document.getElementById("styleInfo").innerHTML =outstr ;
}
function handleClick(oEvent)
{
//alert(this.tagName);
var elem = document.getElementById("txt1");
var distX = Math.abs(oEvent.pageX- lastX);
var distY = Math.abs(oEvent.pageY- lastY);
var distString = "";
if (lastX!=0) distString = ";
dist = (" + distX + "," + distY + ")";
lastX = oEvent.pageX; lastY = oEvent.pageY;
elem.value += "\n At (" + oEvent.pageX + ",
" + oEvent.pageY + ")" + distString;
}
function setPadding(e)
{
//alert("button click");
document.getElementById("B").style.padding=
document.getElementById("padding").value +"px";
getStyleInfo();
placeShadowDiv();
e.stopPropagation(); // event.stopPropagation() works in chrome but fails in firefox
// window.event.cancelBubble = true; // this dose not work in firefox
}
function setMargin(e)
{ document.getElementById("B").style.margin=document.
getElementById("margin").value +"px";
getStyleInfo();
placeShadowDiv();
e.stopPropagation();
// e.cancelBubble = true;
}
function setBoxSizing(boxSize)
{ var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
var elem = document.getElementById("B");
if (is_firefox) elem.style.MozBoxSizing = boxSize; // For FireFox
else elem.style.boxSizing = boxSize; // Works in Chrmoe
placeShadowDiv();
}
function placeShadowDiv()
{ var BstyleInfo = window.getComputedStyle( document.getElementById("B") );
var B_width =parseInt(BstyleInfo.width);
var B_height =parseInt(BstyleInfo.height);
// alert(B_width + "/" + B_height);
var B_topMargin = parseInt(BstyleInfo.marginTop);
var B_leftMargin = parseInt(BstyleInfo.marginLeft);
var B_bottomMargin = parseInt(BstyleInfo.marginBottom);
var B_rightMargin = parseInt(BstyleInfo.marginRight);
var B_topPadding = parseInt(BstyleInfo.paddingTop);
var B_leftPadding = parseInt(BstyleInfo.paddingLeft);
var B_bottomPadding = parseInt(BstyleInfo.paddingBottom);
var B_rightPadding = parseInt(BstyleInfo.paddingRight);
var B_border = parseInt(BstyleInfo.border ); // In FireFox, this gives NaN
if (isNaN(B_border)) B_border = parseInt(BstyleInfo.borderLeftWidth);
var width = B_width + B_leftPadding + B_rightPadding + 2 * B_border +
B_leftMargin + B_rightMargin;
var height = B_height + B_topPadding + B_bottomPadding + 2 * B_border +
B_topMargin + B_bottomMargin;
// alert( width + "/" + height);
DivB_BoxSizing =BstyleInfo.boxSizing;
// alert("DivB_BoxSizing: " + DivB_BoxSizing);
if (DivB_BoxSizing == "padding-box") // padding is parts of width/height
{ width = B_width + 2 * B_border + B_leftMargin + B_rightMargin;
height = B_height + 2 * B_border + B_topMargin + B_bottomMargin;
}
if (DivB_BoxSizing == "border-box") // padding and border are parts of width/height
{ width = B_width + B_leftMargin + B_rightMargin;
height = B_height + B_topMargin + B_bottomMargin;
}
document.getElementById("shadowDiv").style.left= BstyleInfo.left;
document.getElementById("shadowDiv").style.top= BstyleInfo.top;
document.getElementById("shadowDiv").style.width= width +"px";
document.getElementById("shadowDiv").style.height= height +"px";
}
</script>
<style type="text/css" >
body, td { font-size:14pt; }
td { line-height:2.2em; }
select { font-size:.9em; }
input { margin:0; margin-left:16px; font-size:11pt; font-weight:bold; margin-top:6px;}
input[type=button] { padding:4px 8px; width: 120px; }
p { font-size:14pt; font-family:Tahoma; }
div#A {z-index:-1;position:absolute; left:50px;top:320px;height:250px;
width:600px;border: 1px solid #000; background-color:#CCC}
div#B { -moz-box-sizing: content-box; box-sizing:content-box;
z-index:2;position:absolute;left:50px;top:30px;width:400px;height:120px;
border:10px solid lightblue;background-color:green;margin:30px; padding:20px;}
div#shadowDiv {z-index:0;position:absolute;margin:0; padding:0;
background-color:maroon; width:160px; height:150px}
div#topDiv { position:absolute;margin:0; padding:0; top:0;
left:100px; height:30px; border-left:1px solid black;}
div#leftDiv { position:absolute;margin:0; padding:0; left:0;
top:60px; width:50px; border-bottom:1px solid black;}
</style>
</head>
<body onClick="handleClick(event)" >
<div id="A" >
<div id="B">
<div style="background-color:yellow; margin:0;
padding:0;width:100%;height:100%" >
Yellow area is content of <b>div#B</b>
(shows the extent of width and height of <b>div#B</b>
for default box-sizing)<br/><br/>
Green area is padding of <b>div#B</b>
</div>
</div>
<div id="shadowDiv"></div>
<div id="topDiv"> top (30px)</div>
<div id="leftDiv"> left (50px)</div>
</div>
<div onclick="event.stopPropagation()"
style="border-bottom:1px solid gray" >
<table>
<tr>
<td>
<textarea style="font-size:16pt;font-weight:bold;width:440px;
height:160px;" id="txt1" rows="5" cols="30"></textarea>
</td>
<td>Margin and padding for div#B:
<br/>
<input id="margin" size="5" /><
input type="button" onclick="setMargin(event)"
value="Set Margin" />
<br/>
<input id="padding" size="5" /><
input type="button" onclick="setPadding(event)"
value="Set Padding" />
<br/>
<label>Box Sizing
<select onChange="setBoxSizing(this.value)" >
<option value="content-box" >Content Box (default)</option>
<!--<option value="padding-box" >Padding Box</option>-->
<option value="border-box" >Border Box</option>
</select>
</label>
</td>
</tr>
</table>
<p><b>Style Info for div#B:</b><
span id="styleInfo"></span></p>
<p><b><i>Note:</i></b> Offset (left,top, ..)
is measured <b>from </b><i>border of container</i> <b>to </b>
<i>start of margin</i> (and not border) of the enclosed element.
Thus, the position of the "Margin Box"
of the enclosed block is not affected by margin or padding of the container.
</p>
</div>
</body>
</html>
在 HTML 中,我们定义了两个 `div`:`div#A` 和 `div#B`(一个子 `div`),它们都使用了绝对定位。因此(请阅读后面的解释),`div#B` 的顶部和左侧像素值是相对于 `div#A` 的顶部和左侧边框的偏移量。
在 JavaScript 代码中,`handleClick()` 函数会跟踪鼠标点击的位置,并将 (x,y) 坐标打印到文本区域(页面左上角)。它还会打印两个连续位置之间的水平/垂直距离。这样,用户就可以验证 `div#B` 的定位及其四个盒子的尺寸。例如,上面图中显示的坐标是通过点击 `div#B` 的内容盒(黄色区域)的相对两端获得的;因此,“dist = (400,120)”对应于 `div#B` 的宽度和高度。
使用的基本技术
为了区分 `div#B` 的内容盒与其内边距,我将 `div#B` 的内容设置为另一个 `div`(`padding=margin=0`,`width=height=100%`),其背景色与 `div#B` 不同。
为了跟踪 `div#B` 的外边距盒,我使用了一个辅助 `div`(`id="shadowDiv"`)。`shadowDiv` 的左、上、宽和高由 `placeShadowDiv()` 函数计算(该函数在页面加载时首次调用,然后在 `div#B` 的外边距或内边距更改时每次调用)。
最后,`getStyleInfo()` 函数用于计算(并显示)`div#B` 的样式信息。该函数在页面加载时首次调用,然后在内边距/外边距/`box-sizing` 更改时每次调用。
CSS 盒子模型

CSS 盒子模型定义了 HTML 块级元素(如 `div` 和 `p`)的布局。它不适用于内联元素(`span`、`a` 等)。
每个块级元素都由浏览器渲染为一个矩形盒子,包含四部分(盒子)。最内层是内容(或内容盒),周围是内边距(或内边距盒)。边框(或边框盒)包围着内边距;最外层是外边距(外边距盒)。内边距用于指定内容在边框盒内周围的间距量,而外边距用于指定元素与其周围环境之间的间距。对于屏幕显示,内边距和外边距通常以像素为单位。
默认情况下(如果未设置宽度),块级元素会占据浏览器窗口的全部宽度,并在浏览器窗口大小调整时进行调整。元素的高度(如果未设置高度)会调整以适应其内容(用户可能需要滚动)。
当为块级元素指定宽度(或高度)时,它仅应用于内容盒,而不是从边框到边框。虽然这可能违反直觉,但这是默认行为(默认 W3C 盒子模型)。可以通过设置 CSS `box-sizing` 属性来更改此行为。在 CSS3 中,此属性可以设置为“content-box”、“padding-box”或“border-box”。
对于默认盒子模型,块级元素所占用的区域将具有以下总宽度和高度:
总宽度 = 宽度 + 左内边距 + 右内边距 + 左边框 + 右边框 + 左外边距 + 右外边距
总高度 = 高度 + 上内边距 + 下内边距 + 上边框 + 下边框 + 上外边距 + 下外边距
CSS 定位
下表提供了关于 `CSS position` 属性和其他相关属性的基本信息。
属性 | 值和描述 |
位置 | static:默认位置;元素根据正常流进行放置。 忽略 top/left/bottom/right 属性。 |
relative:元素在其正常的静态位置处偏移放置。 | |
absolute:元素在其包含元素内固定位置放置。 | |
fixed:元素在浏览器窗口内固定位置放置(滚动页面时不会移出视图)。 (滚动页面时不会移出视图)。 |
|
top, bottom, left, right | 这些属性决定了元素的(外边距盒)角的位置。 值被解释为偏移量(位移)。 top:垂直偏移(+ 向下移动,- 向上移动) left:水平偏移(+ 向右移动,- 向左移动) bottom:水平偏移(+ 向上移动,- 向下移动) right:垂直偏移(+ 向左移动,- 向右移动) |
float | 此属性通常与“position:static”的块级元素一起使用; 它用于将元素浮动到其容器的左侧或右侧,并让其他 内容环绕浮动元素。 (如果设置了此属性,则会忽略位置和偏移设置。) |
请注意,偏移属性(*top*、*left*、*right*、*bottom*)的值定义了元素(其外边距盒)的哪个边缘要从其容器的相应边缘(内边框)偏移。例如,*top* [left] 的值定义了定位元素的顶部 [左侧] 边缘与其包含块的顶部 [左侧] 边缘之间的距离。符号(+ 或 -)指定了位移的方向(*bottom*/*right* 与 *top*/*left* 相反)。因此,正值会将元素移向包含块的中心;负值会将元素移出包含块。
绝对定位
绝对定位用于将定位元素放置在**相对于某个包含块的固定位置**。top/left/right/bottom 被解释为相对于容器边框的偏移量。容器块是位置值*非 static* 的最近的包含块;如果没有找到这样的块,则假定容器是浏览器窗口。
绝对定位会将元素移出“正常流”;也就是说,绝对定位的元素将占据为其指定的区域,而不管可能存在的其他元素。因此,绝对定位的元素可能会与其他元素重叠(或覆盖)。在这种情况下,可以使用 z-index 属性来控制前后显示顺序。
绝对定位的元素具有边框和内边距。它们也可以有外边距(相对于上面指定的父容器)。
绝对定位的图示
作为绝对定位的说明,请考虑以下 HTML+CSS 代码示例和相关的图形。
div#A {position: absolute; top: 25px; left: 40px;
width: 300px; height: 120px; border: 1px solid #000; background-color:#CCC}
div#B {position: absolute; top: 20px; left: 50px; right: 30px;
bottom: 40px; border: 1px solid #000; background-color:#666}
<div id="A">
<div id="B">
</div>
</div>
在此示例中,我们有两个 `div`:`div#A` 和 `div#B`,其中 `div#B` 是一个子 `div`。两个 `div` 都被绝对定位,因此每个 `div` 将出现在一个固定位置,其 top/left/right/bottom 偏移量相对于某个包含容器的边框。
假定 `div#A` 不包含在任何其他具有非静态位置的块中。因此,`div#A` 的 top/left/right/bottom 偏移量的解释将相对于浏览器窗口。另一方面,`div#B` 的定位将相对于 `div#A`。
绝对定位的一些示例
以下是一个应用 CSS 定位的绝佳示例。其思想是重叠两个 `div` 以创建阴影(或浮雕)效果,如下图所示。
这可以使用以下 HTML+CSS 生成。
<style>
div { font-size:24pt; font-family:georgia; font-style:italic;color:indigo; }
</style>
<div style="position:relative;" >
<div style="position:absolute;left:0; top:0;" >
CSS Positioning is great!</div>
<div style="z-index:-1;position:absolute;left:2px;
top:2px;color:gray" >CSS Positioning is great!</div>
</div>
为了实现上述结果,我们需要其中一个重叠的 `div` 的 left/top 偏移量比另一个稍微(向下和向右移动 2 像素)不同。当 top 和 left 相对于一个包含这两个重叠 `div` 的 `div` 进行测量时,该过程会更容易。包含 `div` 需要将其位置设置为 `absolute` 或 `relative`(后者更适合让包含 `div` 成为正常流的一部分)。
相对定位
相对定位用于将定位元素(内联或块级元素)放置在**偏移位置**,该位置与其在正常流中通常出现的位置不同,使用 top/left/right/bottom 的设置(解释为相对于其正常位置的偏移量)。如果未给出偏移值,则元素将保持在浏览器确定的正常位置。但是,当指定偏移值时,元素将根据给定的偏移量移动(通常需要指定“left, right”中的一个以及“top, bottom”中的一个以避免冲突)。元素最初占据的区域将被留空(这是相对定位的一个显著特征)。
例如,*上标 2* 可以用 HTML 表示为(这可以作为使用<sup>
A<span style="position:relative; top:-.7em;font-size:smaller" >2</span>
使用相对定位,我们可以将文本放置在其他文本的顶部和底部,如下例所示:

这是使用以下 HTML+CSS 生成的。
<style>
div#myDiv { font-size:24pt; font-family:georgia; margin:1em; }
span.smallText { font-size:70%;}
</style>
<diV id="myDiv" >
∑ x<sub>i</sub><span class="smallText"
style="position:relative; top:-1.2em;
left:-2.4em;" ><i>n</i></span>
<span class="smallText" style="position:relative;
top:1.2em;left:-3.2em;" ><i>i</i>=1</span>
</div>
相对定位的常见用途
相对定位的一个常见用途是改变坐标系统的原点(对于 top/left/bottom/right)从浏览器窗口到某个其他元素。这利用了以下事实:
如果块级元素的“position”设置为“position:relative”(或任何非 *static* 的值),它将成为其*绝对定位*的子元素的坐标系统。
以下示例说明了这一点,该示例展示了如何将四个子 `div` 固定到其父级的四个角落。HTML+CSS 代码如下:
<style>
div.childDiv { position: absolute; border:1px solid green;
width:150px;height:150px; text-align:center;line-height:150px; }
</style>
<div style="position:relative; width:300px; height:300px;
border:1px solid black;margin-left:30px;" >
<div class="childDiv" style="background:#FEE;
left:0;top:0" >left:0;top:0</div>
<div class="childDiv" style="background:#EEF;
right:0;top:0" >right:0;top:0</div>
<div class="childDiv" style="background:#EFE;
left:0;bottom:0" >left:0;bottom:0</div>
<div class="childDiv" style="background:#EEE;
right:0;bottom:0" >right:0;bottom:0</div>
</div>
前面的代码渲染如下:

让我们强调一下关于前面示例的几个重要点。
-
如果父 `div` 的位置未指定(或设置为“
static
”),则子 `div` 的坐标将参考浏览器窗口(其当前高度将决定“bottom
”偏移量);子 `div` 将随页面滚动。 -
如果子 `div` 的位置值设置为“
fixed
”,那么它们(无论其父级的定位如何)将固定在浏览器窗口的四个角落,并且它们不会随页面滚动。 -
请注意用于将文本居中在每个子 `div` 内的 CSS 样式。“
text-align:center
”将文本水平居中,“line-height:150px
”将文本垂直居中。
相对定位的另一个良好用途是将元素“切口”到相对于其容器的某个位置,如下图所示。
以上是通过以下 CSS+HTML 代码生成的。
<style>
div.roundDiv { margin:40px 40px; font-size:14pt; font-family:Tahoma; -moz-border-radius:.5em;
-webkit-border-radius:.5em; border-radius:.5em;
}
div#A { border: 1px solid #e6db61; padding:8px; background:#fcf3c5; width:450px;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#fcfcfc), to(#fcf3c5));
background: -moz-linear-gradient(top, #fcfcfc, #fcf3c5);
}
div#B { position:relative; font-weight:bold; left:16px;
top:-32px; border:1px solid lightgray;
margin:0; padding:4px; background-color:#CEFEFE;
width:200px;height:40px;line-height:40px;
background: -webkit-gradient(linear, 0 0, 0 100%,
from(#fcfcfc), to(#e6e6e6));
background: -moz-linear-gradient(top, #fcfcfc, #e6e6e6);
}
</style>
<div id="Div1" class="roundDiv" >
<div id="Div2" class="roundDiv" >
Relative Positioning</div>
<p style="margin-top:-10px;" >
The div for the title is positioned
relatively (left=16px; top=-32px)
</p>
</div>
在没有设置 `top` 和 `left` 的情况下,`div#B` 将被放置在 `div#A` 的左上角附近(`div#A` 的内边距决定了偏移量)。设置“top:-32px”将其向上移动 32 像素,设置“left:16px”将其向右移动 16 像素。
请注意,`div#B` 中的 `p` 元素设置了负的 top-margin,以将其移动到 `div#B` 移动后留出的空白区域。