浏览器原理 · 44/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

Offset、Scroll、Client 属性对比

理解 DOM 元素的三组尺寸和位置属性

问题

offsetscrollclient 这三组属性有什么区别?分别用于什么场景?

解答

三组属性概览

属性组宽高含义位置含义
offset元素完整尺寸(含边框)相对定位父元素的偏移
client可视区域尺寸(不含边框和滚动条)边框宽度
scroll内容完整尺寸(含溢出部分)滚动距离

Offset 系列

const box = document.querySelector('.box');

// offsetWidth = content + padding + h38kz + 滚动条宽度
// offsetHeight = content + padding + h38kz + 滚动条高度
console.log(box.offsetWidth, box.offsetHeight);

// offsetLeft/offsetTop:相对于 offsetParent 的偏移
console.log(box.offsetLeft, box.offsetTop);

// offsetParent:最近的定位祖先(position 非 static)
console.log(box.offsetParent);

Client 系列

const box = document.querySelector('.box');

// clientWidth = content + padding(不含边框和滚动条)
// clientHeight = content + padding(不含边框和滚动条)
console.log(box.clientWidth, box.clientHeight);

// clientLeft/clientTop:左边框和上边框的宽度
console.log(box.clientLeft, box.clientTop);

Scroll 系列

const box = document.querySelector('.box');

// scrollWidth/scrollHeight:内容的完整尺寸(包括溢出不可见部分)
console.log(box.scrollWidth, box.scrollHeight);

// scrollLeft/scrollTop:已滚动的距离(可读写)
console.log(box.scrollLeft, box.scrollTop);

// 滚动到指定位置
box.scrollTop = 100;

可视化示例

<!DOCTYPE html>
<html>
<head>
  <style>
    .container {
      position: relative;
      padding: 20px;
    }
    .box {
      width: 200px;
      height: 150px;
      padding: 20px;
      border: 10px solid #333;
      margin: 30px;
      overflow: auto;
    }
    .content {
      width: 300px;
      height: 400px;
      background: linear-gradient(#f0f0f0, #ccc);
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="box">
      <div class="content">滚动内容</div>
    </div>
  </div>

  <script>
    const box = document.querySelector('.box');
    
    // Offset:元素完整尺寸
    // 200 + 20*2 + 10*2 = 260
    console.log('offsetWidth:', box.offsetWidth);  // 260
    console.log('offsetHeight:', box.offsetHeight); // 210
    console.log('offsetLeft:', box.offsetLeft);     // 30(margin)
    
    // Client:可视区域(不含边框和滚动条)
    // 200 + 20*2 = 240(减去滚动条约17px)
    console.log('clientWidth:', box.clientWidth);   // ~223
    console.log('clientHeight:', box.clientHeight); // 190
    console.log('clientLeft:', box.clientLeft);     // 10(边框宽度)
    
    // Scroll:内容完整尺寸
    console.log('scrollWidth:', box.scrollWidth);   // 300(内容宽度)
    console.log('scrollHeight:', box.scrollHeight); // 440(内容高度+padding)
    
    // 监听滚动
    box.addEventListener('scroll', () => {
      console.log('scrollTop:', box.scrollTop);
    });
  </script>
</body>
</html>

常见应用场景

// 1. 判断元素是否滚动到底部
function isScrolledToBottom(el) {
  return el.scrollHeight - el.scrollTop === el.clientHeight;
}

// 2. 获取元素在页面中的绝对位置
function getAbsolutePosition(el) {
  let left = 0, top = 0;
  while (el) {
    left += el.offsetLeft;
    top += el.offsetTop;
    el = el.offsetParent;
  }
  return { left, top };
}

// 3. 判断是否有滚动条
function hasScrollbar(el) {
  return el.scrollHeight > el.clientHeight;
}

// 4. 获取滚动条宽度
function getScrollbarWidth(el) {
  return el.offsetWidth - el.clientWidth - el.clientLeft * 2;
}

关键点

  • offsetWidth/Height:元素占据的完整空间,包含 content + padding + border
  • clientWidth/Height:元素内部可视区域,包含 content + padding,不含边框和滚动条
  • scrollWidth/Height:元素内容的完整尺寸,包括溢出隐藏的部分
  • scrollTop/Left 是可读写的,其他属性都是只读
  • 判断滚动到底:scrollHeight - scrollTop === clientHeight