Skip to content

body 元素

<body> 元素是 HTML 文档中所有用户可见内容的容器。页面中显示的文字、图片、链接、表格、表单、音视频等所有内容,都必须放在 <body> 内部。

前置知识

阅读本节前,建议先了解:head 元素

基础概念

body 的作用

<body> 元素定义了 HTML 文档的内容区域。如果说 <head> 是文档的"幕后配置",那么 <body> 就是文档的"舞台"——所有用户能看到的、能交互的内容都在这里呈现。

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>body 元素演示</title>
</head>
<body>
  <!-- 用户能看到的所有内容都在这里 -->
  <header>
    <h1>网站标题</h1>
    <nav>导航菜单</nav>
  </header>
  <main>
    <p>这是页面的主要内容区域。</p>
  </main>
  <footer>
    <p>页脚信息</p>
  </footer>
</body>
</html>

body 的层级关系

Document
 └── html (根元素)
      ├── head (元信息)
      │    ├── meta
      │    ├── title
      │    └── link
      └── body (内容区域) ← 本节重点
           ├── header
           │    ├── h1
           │    └── nav
           ├── main
           │    ├── article
           │    ├── section
           │    └── aside
           └── footer

body 与 document.body

在 JavaScript 中,document.body 是访问 <body> 元素的快捷方式:

javascript
// 两种方式等价
var body1 = document.body;
var body2 = document.getElementsByTagName('body')[0];
var body3 = document.querySelector('body');

注意

如果脚本在 <head> 中同步执行(没有 defer 或 async),此时 document.body 可能为 null,因为 <body> 还未被解析。

语法

基本语法

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>页面标题</title>
</head>
<body>
  <!-- 页面内容 -->
</body>
</html>

body 的全局属性

<body> 元素除了支持所有 HTML 全局属性外,还支持以下专属的事件属性:

窗口事件属性

属性说明触发时机
onload页面加载完成所有资源(图片、样式表、脚本等)加载完毕
onunload页面卸载用户离开页面时
onbeforeunload页面即将卸载在 unload 之前触发,可用于提示用户保存
onpageshow页面显示页面加载或从缓存中恢复时
onpagehide页面隐藏用户离开页面或页面被缓存时
onresize窗口大小改变浏览器窗口尺寸变化时
onscroll页面滚动用户滚动页面时
onerror加载错误资源加载失败时

表单事件属性

属性说明
onfocus元素获得焦点
onblur元素失去焦点
onreset表单重置
onsubmit表单提交
onchange元素值改变
oninput用户输入时
onselect文本被选中
oninvalid表单验证失败

拖拽事件属性

属性说明
ondrag元素被拖动
ondragstart拖动开始
ondragend拖动结束
ondragover拖过目标元素
ondragenter拖入目标元素
ondragleave拖离目标元素
ondrop在目标元素上放下
onscroll滚动事件

剪贴板事件属性

属性说明
oncopy复制内容
oncut剪切内容
onpaste粘贴内容

键盘事件属性

属性说明
onkeydown按下键盘按键
onkeypress按下字符键(已废弃)
onkeyup释放键盘按键

鼠标事件属性

属性说明
onclick单击
ondblclick双击
onmousedown鼠标按下
onmouseup鼠标释放
onmouseover鼠标移入
onmousemove鼠标移动
onmouseout鼠标移出
onmouseenter鼠标进入(不冒泡)
onmouseleave鼠标离开(不冒泡)
onwheel滚轮滚动
oncontextmenu右键菜单

关于内联事件属性

虽然在 <body> 标签上使用事件属性(如 onload="init()")是合法的,但不推荐这种写法。推荐使用 JavaScript 的 addEventListener 方法来绑定事件,以实现关注点分离。

详细说明

body 的默认样式

浏览器为 <body> 元素提供了默认样式,不同浏览器的默认值略有差异:

css
/* 大多数浏览器的 body 默认样式 */
body {
  display: block;
  margin: 8px;          /* 浏览器默认有 8px 的外边距 */
  /* Chrome/Safari 的默认值 */
}

通常在开发中,我们会重置 body 的默认样式:

css
/* 常见的 CSS 重置 */
body {
  margin: 0;
  padding: 0;
  font-family: system-ui, -apple-system, sans-serif;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
}

body 的背景与前景

<body> 元素是设置页面背景色和默认文字颜色的标准位置:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>body 背景</title>
  <style>
    /* 通过 CSS 设置 body 背景 */
    body {
      background-color: #f5f5f5;
      color: #333;
      background-image: url('background.jpg');
      background-size: cover;
      background-position: center;
      background-attachment: fixed;
      min-height: 100vh;
    }
  </style>
</head>
<body>
  <h1>页面内容</h1>
</body>
</html>

补充说明

根据 HTML 规范,如果 <body> 没有设置背景,浏览器会将 <html> 元素的背景应用到整个画布。但为了清晰和可预测性,建议直接在 <body> 上设置背景样式。

onload 事件详解

onload<body> 最常用的事件属性,它在页面所有资源加载完成后触发。

使用内联事件属性(不推荐)

html
<!-- 不推荐:内联事件处理器 -->
<body onload="init()">

使用 JavaScript addEventListener(推荐)

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>onload 事件</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      max-width: 700px;
      margin: 40px auto;
      padding: 0 20px;
    }
  </style>
</head>
<body>
  <h1>页面加载事件</h1>
  <p id="status">加载中...</p>
  <img src="https://picsum.photos/800/400" alt="演示图片" id="demo-img">

  <script>
    // 推荐:使用 addEventListener
    window.addEventListener('load', function() {
      document.getElementById('status').textContent =
        '所有资源已加载完成!(包括图片)';
    });

    // DOMContentLoaded:DOM 解析完成即触发,不等待图片等资源
    document.addEventListener('DOMContentLoaded', function() {
      console.log('DOM 已解析完成,但图片可能还在加载。');
    });
  </script>
</body>
</html>

load 与 DOMContentLoaded 的区别

特性loadDOMContentLoaded
触发时机所有资源加载完成(图片、CSS、JS、iframe)DOM 解析完成(不等待图片和样式表)
触发对象windowdocument
等待图片
等待 CSS否(但同步脚本会阻塞解析)
等待 iframe
适用场景需要操作图片尺寸等依赖资源就绪的场景需要尽早操作 DOM 的场景
性能触发较晚触发较早
javascript
// load 事件:等待所有资源
window.addEventListener('load', function() {
  var img = document.getElementById('my-image');
  // 此时可以安全地获取图片的宽高
  console.log('图片宽度:', img.naturalWidth);
  console.log('图片高度:', img.naturalHeight);
});

// DOMContentLoaded 事件:DOM 解析完成
document.addEventListener('DOMContentLoaded', function() {
  // 此时 DOM 已可操作,但图片可能未加载
  var list = document.getElementById('user-list');
  // 可以安全地操作 DOM 元素
  list.innerHTML = '<li>用户 1</li><li>用户 2</li>';
});

body 中的内容组织

<body> 内部通常按照语义化标签来组织内容:

html
<body>
  <!-- 页面头部 -->
  <header>
    <h1>网站名称</h1>
    <nav>
      <ul>
        <li><a href="/">首页</a></li>
        <li><a href="/about">关于</a></li>
        <li><a href="/contact">联系</a></li>
      </ul>
    </nav>
  </header>

  <!-- 主要内容 -->
  <main>
    <article>
      <h2>文章标题</h2>
      <p>文章内容...</p>
    </article>

    <aside>
      <h3>侧边栏</h3>
      <p>相关链接和推荐内容</p>
    </aside>
  </main>

  <!-- 页面底部 -->
  <footer>
    <p>&copy; 2024 网站名称. 保留所有权利。</p>
  </footer>
</body>

body 与 iframe

在 iframe 中,内部页面也有自己的 <body> 元素:

html
<!-- 父页面 -->
<body>
  <h1>父页面</h1>
  <iframe src="child.html" id="my-iframe"></iframe>

  <script>
    var iframe = document.getElementById('my-iframe');

    // 访问 iframe 内部的 body
    iframe.addEventListener('load', function() {
      var innerBody = iframe.contentDocument.body;
      innerBody.style.backgroundColor = '#ffffcc';
    });
  </script>
</body>

跨域限制

如果 iframe 加载的是跨域页面,由于同源策略限制,父页面无法访问 iframe 内部的 DOM。

实战示例

页面加载进度指示器

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>加载进度指示器</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: system-ui, sans-serif;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      background-color: #f8f9fa;
    }
    /* 加载遮罩 */
    #loading-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: white;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      z-index: 9999;
      transition: opacity 0.5s ease;
    }
    #loading-overlay.hidden {
      opacity: 0;
      pointer-events: none;
    }
    .progress-bar {
      width: 300px;
      height: 6px;
      background: #e0e0e0;
      border-radius: 3px;
      overflow: hidden;
      margin-top: 20px;
    }
    .progress-fill {
      height: 100%;
      background: linear-gradient(90deg, #0066cc, #00aaff);
      border-radius: 3px;
      width: 0%;
      transition: width 0.3s ease;
    }
    .loading-text {
      margin-top: 12px;
      color: #666;
      font-size: 14px;
    }
    /* 主内容 */
    #main-content {
      text-align: center;
      display: none;
    }
    #main-content h1 {
      font-size: 2em;
      margin-bottom: 10px;
    }
  </style>
</head>
<body>
  <!-- 加载遮罩 -->
  <div id="loading-overlay">
    <div class="progress-bar">
      <div class="progress-fill" id="progress-fill"></div>
    </div>
    <p class="loading-text" id="loading-text">正在加载资源...</p>
  </div>

  <!-- 主内容(初始隐藏) -->
  <div id="main-content">
    <h1>页面加载完成</h1>
    <p>所有资源已成功加载,欢迎访问!</p>
    <img src="https://picsum.photos/600/300" alt="演示图片">
  </div>

  <script>
    var progressFill = document.getElementById('progress-fill');
    var loadingText = document.getElementById('loading-text');
    var overlay = document.getElementById('loading-overlay');
    var mainContent = document.getElementById('main-content');

    // 模拟加载进度
    var progress = 0;
    var timer = setInterval(function() {
      progress += Math.random() * 15 + 5;
      if (progress > 90) progress = 90; // 停在 90% 等待真实加载
      progressFill.style.width = progress + '%';
    }, 200);

    // DOM 加载完成
    document.addEventListener('DOMContentLoaded', function() {
      loadingText.textContent = '正在渲染页面...';
    });

    // 所有资源加载完成
    window.addEventListener('load', function() {
      clearInterval(timer);
      progressFill.style.width = '100%';
      loadingText.textContent = '加载完成!';

      setTimeout(function() {
        overlay.classList.add('hidden');
        mainContent.style.display = 'block';
      }, 500);
    });
  </script>
</body>
</html>

实时显示滚动信息

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>滚动信息</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      margin: 0;
      padding: 0;
      line-height: 1.6;
    }
    /* 固定信息栏 */
    #scroll-info {
      position: fixed;
      bottom: 0;
      left: 0;
      width: 100%;
      background: rgba(0, 0, 0, 0.85);
      color: white;
      padding: 10px 20px;
      font-size: 14px;
      font-family: monospace;
      z-index: 1000;
    }
    #scroll-info span {
      margin-right: 20px;
    }
    /* 生成足够多的内容以便滚动 */
    .content-section {
      min-height: 100vh;
      padding: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 1.5em;
      color: white;
    }
    .section-1 { background: linear-gradient(135deg, #667eea, #764ba2); }
    .section-2 { background: linear-gradient(135deg, #f093fb, #f5576c); }
    .section-3 { background: linear-gradient(135deg, #4facfe, #00f2fe); }
    .section-4 { background: linear-gradient(135deg, #43e97b, #38f9d7); }
    .section-5 { background: linear-gradient(135deg, #fa709a, #fee140); }
  </style>
</head>
<body>
  <div id="scroll-info">
    <span>scrollY: <b id="scroll-y">0</b>px</span>
    <span>窗口高度: <b id="win-height">0</b>px</span>
    <span>文档高度: <b id="doc-height">0</b>px</span>
    <span>滚动百分比: <b id="scroll-percent">0</b>%</span>
  </div>

  <div class="content-section section-1"><h1>第一部分</h1></div>
  <div class="content-section section-2"><h1>第二部分</h1></div>
  <div class="content-section section-3"><h1>第三部分</h1></div>
  <div class="content-section section-4"><h1>第四部分</h1></div>
  <div class="content-section section-5"><h1>第五部分 - 到底了!</h1></div>

  <script>
    var scrollYEl = document.getElementById('scroll-y');
    var winHeightEl = document.getElementById('win-height');
    var docHeightEl = document.getElementById('doc-height');
    var scrollPercentEl = document.getElementById('scroll-percent');

    // 使用 passive 事件监听器提高滚动性能
    window.addEventListener('scroll', function() {
      var scrollY = window.scrollY || window.pageYOffset;
      var winHeight = window.innerHeight;
      var docHeight = document.documentElement.scrollHeight;
      var maxScroll = docHeight - winHeight;
      var percent = maxScroll > 0 ? Math.round((scrollY / maxScroll) * 100) : 0;

      scrollYEl.textContent = scrollY;
      winHeightEl.textContent = winHeight;
      docHeightEl.textContent = docHeight;
      scrollPercentEl.textContent = percent;
    }, { passive: true });
  </script>
</body>
</html>

注意事项

html
<!-- 错误!meta 和 link 应该在 head 中 -->
<body>
  <meta charset="UTF-8">
  <title>错误示例</title>
  <link rel="stylesheet" href="style.css">
  <h1>内容</h1>
</body>

<!-- 正确 -->
<head>
  <meta charset="UTF-8">
  <title>正确示例</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>内容</h1>
</body>

虽然浏览器能容错处理这种情况,但这是不规范的写法。

2. 避免在 body 上直接设置样式属性

html
<!-- 不推荐:使用 HTML 属性设置样式 -->
<body bgcolor="#ffffff" text="#333333" link="#0066cc" vlink="#663399" alink="#ff0000">

<!-- 推荐:使用 CSS 设置样式 -->
<body>
  <style>
    body {
      background-color: #ffffff;
      color: #333333;
    }
    a { color: #0066cc; }
    a:visited { color: #663399; }
    a:active { color: #ff0000; }
  </style>

bgcolortextlinkvlinkalink 等 body 属性已在 HTML5 中被废弃。

3. 避免过深的嵌套

虽然 <body> 可以包含任意深度的嵌套元素,但过深的嵌套会影响性能和可读性:

html
<!-- 不推荐:过深的嵌套 -->
<body>
  <div>
    <div>
      <div>
        <div>
          <div>
            <p>内容</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

建议嵌套层级不超过 5~7 层。

4. 确保 body 有足够的可访问性

html
<!-- 推荐:使用语义化标签 -->
<body>
  <a href="#main-content" class="skip-link">跳转到主要内容</a>
  <header>...</header>
  <main id="main-content">...</main>
  <footer>...</footer>
</body>

最佳实践

1. 使用 CSS 重置 body 默认样式

css
/* 推荐:CSS 重置或 Normalize.css */
body {
  margin: 0;
  padding: 0;
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

2. 使用语义化标签组织内容

html
<body>
  <header>
    <nav>...</nav>
  </header>
  <main>
    <article>...</article>
    <aside>...</aside>
  </main>
  <footer>...</footer>
</body>

3. 使用 JavaScript 事件监听器,而非内联事件

html
<!-- 不推荐 -->
<body onload="init()" onscroll="handleScroll()">

<!-- 推荐 -->
<script>
  window.addEventListener('load', init);
  window.addEventListener('scroll', handleScroll, { passive: true });
</script>

4. 为 body 设置 min-height

css
/* 确保页面内容不足时,背景色仍覆盖整个视口 */
body {
  min-height: 100vh;
}
html
<body>
  <a href="#main" style="position:absolute; left:-9999px;">
    跳转到主要内容
  </a>
  <main id="main">
    <!-- 主要内容 -->
  </main>
</body>

下一节

继续学习:title 标题

参考链接