HTML中各种 div 位置距离关系
一. 盒模型图片展示:
二. 位置距离计算属性
-
offsetWidth, offsetHeight
获取盒子的宽度/高度(包括盒子的border,padding和内容width/height),不包括外边距 -
offsetLeft
获取盒子当前位置(左上角)距离自己最近定位的父元素左侧的距离,如果没有最近的定位的父元素,则相当于HTML -
offsetTop
获取盒子当前位置距离自己最近定位的父元素顶部的距离,如果没有最近的定位的父元素,则相当于HTML -
clientWidth,clientHeight
获取盒子的可视区域宽度、高度,包括padding在内,不包括border,这里是不包括滚动条的情况,如果有滚动条,发现盒子的宽度(width)从100变成83,可知滚动条的宽度为17px,而且是占据盒子内容的宽度,除了可视宽高,似乎其他都没有影响。
所谓的可视区域,个人理解就是一个盒子里面,能够展示出被人看见的内容区域 -
clientLeft
获取盒子的左边框的宽度,可理解为可视区域和左侧边(这个左侧边不是border)之间的距离 -
clientTop
获取盒子的上边框的宽度,可理解为可视区域和上侧边(这个上侧边不是border)之间的距离 -
scrollWidth,scrollHeight
获取盒子内容里面元素占据的真正宽度、高度 -
scrollLeft,scrollTop
滚动条距离左侧边,上侧边的距离
滚动条的最大滚动高度为滚动高度(scrollHeight) - 盒子可视高度(clientHeight) -
innerWidth,innerHeight
窗口宽度、高度,也可以理解为window窗口的可视区域宽度、高度
let IH = window.innerHeight
// 标准模式下
if (document.documentElement) {
let IH = document.documentElement.clientHeight
// 怪异模式
} else {
let IH = document.body.clientHeight
}
clientX & clientY
鼠标点击或者触屏时,点击位置距离window可视区域左上角的(0, 0)的坐标距离
<div @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)">
</div>
touchStart(e){
let info = this.dropDownInfo;
info.startX = e.targetTouches[0].clientX;
info.startY = e.targetTouches[0].clientY;
console.log(e)
},
-
pageX & pageY
正常情况下,window的可视区域的是不变的,所以相对于可视区域的坐标也就不会变,无论怎么点,clientX和clientY都是一样的。但是从page就可以知道,这个是页面坐标,也就是点击的页面距离window可视区域左上角的坐标距离。
一般可视区域是固定的,但是页面大小就不一定是固定的,如果有滚动条,说面页面大小超过了可视区域,这时候点击滚动隐藏区域,pageX&pageY肯定是大于clientX&clientY -
screenX & screenY
点击的位置距离屏幕的左上角的上左距离
三. 应用场景
-
swiper左右滑动切换列表的内部嵌套slide滑块
问题:内部slide滑块滑动事件与swiper左右滑动切换事件冲突,影响性能
解决办法:监听滑块滑到触左、触右,触左时可触发swiper左切换,触右时可触发swiper右切换,除此之外,内部滑动阻止冒泡
<div @scroll="scrollEvent" @touchstart="touchStart($event)" @touchmove="touchMove($event)" class="slide">
<div class="slide__item"></div>
<div class="slide__item"></div>
<div class="slide__item"></div>
</div>
data() {
return {
dropDownInfo: {
startX: 0,
startY: 0,
isDropDown: false, // 是否下拉
isBorder: false,
},
leftCanChange: true, // 靠左touch可以触发父级swiper切换事件,默认true
rightCanChange: false, // 靠右touch可以触发父级swiper切换事件,默认false
}
},
methods: {
scrollEvent(e){
const slideWidth = e.target.scrollWidth; // 盒子内容里面元素占据的真正宽度
const offsetWidth = e.target.offsetWidth; // 盒子的真实宽度,不包括左右margin外边距
const scrollLeft = e.target.scrollLeft; // 滚动条滚动的宽度
if (scrollLeft <= 0) {
// 触左
this.leftCanChange = true
} else if (scrollLeft + offsetWidth >= slideWidth) {
// 触右(滚动条可滚动的宽度 + 盒子的真实宽度 >= 盒子内容元素的真实宽度)
this.rightCanChange = true
} else {
this.leftCanChange = false
this.rightCanChange = false
}
},
/**
* 触摸开始
*/
touchStart(e){
const info = this.dropDownInfo;
info.startX = e.targetTouches[0].pageX;
info.startY = e.targetTouches[0].pageY;
},
/**
* 触摸滑动时
*/
touchMove(e){
let info = this.dropDownInfo;
const X = e.targetTouches[0].pageX - info.startX;
const disX = Math.abs(X)
const disY = e.targetTouches[0].pageY - info.startY
// 左右滑动且列表数据大于1,阻止冒泡触发父级swiper切换
if ((disX > disY) && this.options.length > 1) {
// 除了触左,右滑 || 触右,左滑不阻止冒泡,防止触发父级swiper切换
if (!(this.leftCanChange && X > 0 || this.rightCanChange && X < 0)) {
e.stopPropagation();
}
}
},
}
.slide{
position: relative;
overflow: hidden;
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
background: #f5f5f5;
}
.slide__item{
display: inline-block;
width: 6.58rem;
min-height: 3rem;
margin: 0 0.08rem;
vertical-align: top;
background: #ffffff;
}
2. 监听元素是否在可视区
mounted() {
window.addEventListener("scroll", this.handleScroll, false);
},
methods: {
/**
* 页面滚动
*/
handleScroll(e) {
let el = document.getElementById("elementId"); // 需要监听的元素
let isVideoVisible = this.$util.isElementVisible(el);
}
},
util.js
/**
* 元素是否在可视区
* @param el
* @returns {boolean|boolean | *}
*/
function isElementVisible(el) {
const rect = el.getBoundingClientRect();
const vWidth = window.innerWidth || document.documentElement.clientWidth;
const vHeight = window.innerHeight || document.documentElement.clientHeight;
const efp = function (x, y) {
return document.elementFromPoint(x, y);
};
if (rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight) return false;
return (
el.contains(efp(rect.left, rect.top))
|| el.contains(efp(rect.right, rect.top))
|| el.contains(efp(rect.right, rect.bottom))
|| el.contains(efp(rect.left, rect.bottom))
);
}
以上代码只对上下滚动监听有用
在一个页面的slide左右滚动滑块中监听的话要用别的方法
**getBoundingClientRect();**获取元素距离浏览器周边的位置的方法
<div @scroll="scrollEvent" class="council__slide">
<div
v-for="(item, index) in options"
:key="'list-'+ index"
class="council__slide-item">
</div>
</div>
.council__slide-box{
position: relative;
overflow: hidden;
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
background: transparent;
}
}
.council__slide-item{
display: inline-block;
width: 6.78rem;
height: 3.86rem;
border-radius: 0.4rem;
overflow: hidden;
margin: 0 0.1rem;
}
created () {
this.throttleScroll = $util.throttle(this.pageScroll, 100);
},
mounted () {},
methods: {
scrollEvent(){
this.throttleScroll();
},
pageScroll () {
let el = document.getElementById('playVideo');
const rectLeft = el.getBoundingClientRect(); // 获取元素距离浏览器周边的距离
//
if (Math.abs(rectLeft.left) >= 187) {
...
}
}
},
}
<input type="text" id="inp" />
<script type="text/javascript">
var box = document.getElementById( "inp" );
alert(box.getBoundingClientRect().top);
alert(box.getBoundingClientRect().right);
alert(box.getBoundingClientRect().bottom);
alert(box.getBoundingClientRect().left);
function getRect( elements ){
var rect = elements.getBoundingClientRect();
var clientTop = document.documentElement.clientTop;
var clientLeft = document.documentElement.clientLeft;
return { // 兼容ie多出的两个px
top : rect.top - clientTop, // 距离顶部的位置
bottom : rect.bottom - clientTop, // 距离顶部加上元素本身的高度就等于bottom的位置
left : rect.left - clientLeft, // 距离左边的位置
right : rect.right - clientLeft // 距离右边的位置就是 距离左边的位置加上元素本身的宽度
};
};
</script>