65.9K
CodeProject 正在变化。 阅读更多。
Home

CSS 盒子模型和定位

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (13投票s)

2013年3月28日

CPOL

10分钟阅读

viewsIcon

71389

downloadIcon

349

本文介绍了 CSS 盒子模型和定位的基本信息。

CSS Box Model Image

引言

CSS 盒子模型和 CSS 定位是网页设计中的两个基本概念。理解这些概念对于掌握页面布局和其他网页设计问题至关重要。

在本文中,我将介绍一个演示网页(可以通过 这里 运行),其中包含一些 JavaScript 代码,以帮助理解 W3C 盒子模型和 CSS 定位。该演示在最新版本的 FireFox 和 Chrome 中运行正常。

在我于 KFUPM(我任教的学校)的一个 Web 工程与设计课程中讲授这些概念时,我感到需要一些交互式过程来帮助理解这些概念,这就是这个想法的由来。

本文遵循同样的精神,提供了讲解和一些有用的示例。

文章分为三个部分:

  1. 演示工作原理
  2. CSS 盒子模型
  3. 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 盒子模型

Box model image

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>

Absolute Positioning Example 1

在此示例中,我们有两个 `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` 以创建阴影(或浮雕)效果,如下图所示。

Absolute Positioning Example 2

这可以使用以下 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>

使用相对定位,我们可以将文本放置在其他文本的顶部和底部,如下例所示:

Relative positioning example

这是使用以下 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>

前面的代码渲染如下:

Relative positioning example

让我们强调一下关于前面示例的几个重要点。

  • 如果父 `div` 的位置未指定(或设置为“static”),则子 `div` 的坐标将参考浏览器窗口(其当前高度将决定“bottom”偏移量);子 `div` 将随页面滚动。

  • 如果子 `div` 的位置值设置为“fixed”,那么它们(无论其父级的定位如何)将固定在浏览器窗口的四个角落,并且它们不会随页面滚动。

  • 请注意用于将文本居中在每个子 `div` 内的 CSS 样式。“text-align:center”将文本水平居中,“line-height:150px将文本垂直居中。

相对定位的另一个良好用途是将元素“切口”到相对于其容器的某个位置,如下图所示。

Relative positioning example

以上是通过以下 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` 移动后留出的空白区域。

有用链接

© . All rights reserved.