# DOM 获取坐标的属性详解
## 1. 元素相对于视口的坐标
### clientLeft/clientTop
- 表示元素左边框/上边框的宽度(包括滚动条宽度)
- 不包括外边距和内边距
- 只读属性
```javascript
const elem = document.getElementById('example');
console.log(elem.clientLeft, elem.clientTop);
clientWidth/clientHeight
- 元素内容区+内边距的宽度/高度
- 不包括边框、外边距和滚动条
- 只读属性
console.log(elem.clientWidth, elem.clientHeight);
offsetLeft/offsetTop
- 元素相对于最近定位祖先元素的左/上偏移
- 如果祖先元素都没有定位,则相对于文档
- 包括边框但不包括外边距
console.log(elem.offsetLeft, elem.offsetTop);
offsetWidth/offsetHeight
- 元素整体尺寸,包括内容、内边距、边框和滚动条
- 不包括外边距
- 只读属性
console.log(elem.offsetWidth, elem.offsetHeight);
2. 元素相对于文档的坐标
getBoundingClientRect()
- 返回一个DOMRect对象,包含元素相对于视口的位置和尺寸
- 包括x/y(左上角坐标)、width/height、top/right/bottom/left
- 会考虑CSS变换(transform)的影响
const rect = elem.getBoundingClientRect();
console.log(rect.left, rect.top);
console.log(rect.width, rect.height);
计算文档坐标
- 通过结合getBoundingClientRect()和window.scrollX/scrollY
- 可以获取元素相对于整个文档的绝对位置
const docX = rect.left + window.scrollX;
const docY = rect.top + window.scrollY;
3. 鼠标/触摸事件坐标
event.clientX/clientY
event.pageX/pageY
event.screenX/screenY
elem.addEventListener('click', (e) => {
console.log(e.clientX, e.clientY);
console.log(e.pageX, e.pageY);
console.log(e.screenX, e.screenY);
});
4. 滚动相关坐标
window.scrollX/scrollY
- 文档当前水平/垂直滚动距离
- 别名:window.pageXOffset/pageYOffset
element.scrollLeft/scrollTop
console.log(window.scrollX, window.scrollY);
console.log(elem.scrollLeft, elem.scrollTop);
5. 视口尺寸
window.innerWidth/innerHeight
document.documentElement.clientWidth/clientHeight
console.log(window.innerWidth, window.innerHeight);
console.log(document.documentElement.clientWidth, document.documentElement.clientHeight);
最佳实践建议
- 获取元素位置优先使用getBoundingClientRect()
- 考虑性能:避免在滚动/调整大小事件中频繁获取坐标
- 注意坐标系差异:视口坐标 vs 文档坐标
- 移动端开发要处理设备像素比(DPR)问题
- 复杂布局考虑使用IntersectionObserver替代滚动监听
兼容性注意事项
- IE8及以下不支持getBoundingClientRect()的width/height
- 旧浏览器可能需要polyfill
- 移动端touch事件坐标处理差异
- 某些CSS变换可能影响坐标计算结果
性能优化技巧
- 缓存DOMRect对象避免重复计算
- 使用requestAnimationFrame优化滚动相关计算
- 对于固定位置的元素,可以只计算一次坐标
- 考虑使用CSS sticky定位替代JS实现的粘性效果