一、JS设置元素样式
- box.style.xxx = xxxx //写在元素的行内样式上
- box.style.cssText = "width:100px;height:100px;" //批量加
- box.className = xxx //给元素设置样式类名,从而让其拥有某些样式
- box.classList.add(xxx) //为使样式类名不覆盖原有的设置方法
box.classList.remove(xxx) //移除类名
box.classList.replace(xxx,xxxx) //替换
box.classList.contains(xxx) //包含
box.classList.toggle(xxx) //原来有删除,没有添加
二、JS获取元素样式
1、box.style.xxx
获取写在元素"行内"上的样式【特点:样式只有写在行内上才可以获取】
2、window.getComputedStyle([element],[伪类 after/before])
返回的是包含当前元素所有“经过浏览器计算后”的样式对象【但凡元素经过浏览器渲染,所有样式都是被计算的;所以无论写在行内还是样式表中,写或者不写样式,样式都可以获取到】
3、ele.getBoundingClientRect()
- 作用: 返回了一个对象,包含当前元素和可视窗口的位置信息\
- left/right 盒子左边框/右边框距离可视窗口“左边”的距离\
- top/bottom 盒子上边框/下边框距离可视窗口“上边”的距离
三、JS盒子模型属性:获取元素的相关样式
1、clientWidth/clientHeight
获取元素的可视区域宽高【真实内容+左右padding】,不受内容溢出的影响
2、clientTop/clientLeft
获取元素上边框和左边框的宽度
3、offsetWidth/offsetHeight
clientWidth/clientHeight基础上加上“左右/上下边框”即【真实内容+左右padding+左右border】。
4、scrollWidth/scrollHeight
在没有内容溢出的情况下,获取的值和clientWidth/clientHeight相同;
在有内容溢出的情况下,获取的是真实宽高(包含溢出内容),获取的是约等于的值【因为根据浏览器设置overflow的值不同,获取的结果也不尽相同;不同浏览器下获取到的值也不尽相同】。
5、scrollTop/scrollLeft
获取盒子(或者页面)卷去的高度和宽度【这两个属性是13个属性中唯一“可读写”的,其余11个属性只是“可读”的】。
6、offsetTop/offsetLeft
获取元素距离其父级参照物的距离【上边距/左边距 距离范围是:元素的外边框-->父级参照物的内边框】
7、offsetParent
获取元素的父级参照物【同一个平面(文档流)中,最外层元素是内层所有元素的父级参照物】。
- 默认情况下,元素的父级参照物是body【body.offsetParent===null】
- 想要修改父级参照物,可以修改元素的“定位规则”【目的:让其脱离文档流】(利用定位)
8、助理解
- clientWidth...
9、总结:
1)只有scrollTop/scrollLeft 是“可读写”的,其余属性都是“只读”的;
2)clientWidth/clientHeight、clientTop/clientLeft、offsetWidth/offsetHeight、scrollWidth/scrollHeight 都是获取元素本身的样式(例如:宽高和边框等);而offsetTop/offsetLeft/offsetParent获取的都是元素距离别人的位置
3)操作浏览器的盒模型都是基于 document.documentElement(HTML元素)进行操作的;
4)获取到的样式都是经过四舍五入的整数值,不会出现小数!!
// 获取元素距离BODY的左偏移/上偏移「不论元素父参照物是谁」
const offset = function offset(ele) {
// 先获取本身的偏移
let left = ele.offsetLeft,
top = ele.offsetTop,
parent = ele.offsetParent;
// 只要父参照物还在,并且不是BODY,则重复下述步骤
while (parent && parent.tagName !== "BODY") {
// 在之前的基础上把父参照物的边框和偏移加上
let { clientLeft, offsetLeft, clientTop, offsetTop } = parent;
left += clientLeft + offsetLeft;
top += clientTop + offsetTop;
// 获取父参照物的父参照物
parent = parent.offsetParent;
}
return {
left,
top
};
};
四、IntersectionObserver(ES6新增的一个内置类)
- 作用:监听事件
- 用法:创建绑定事件后直接调用 ob.observe(监视元素) ob.unobverse(移除监听)
- 不兼容IE浏览器
- 执行过程:\
- new InteresectionObserver(callback)创建一个它的实例;\
- 创建一个监听器,用来监听一个或者多个DOM元素和浏览器可视窗口的交叉状态和信息;\
- 当交叉状态(出现/离开可视窗口)发生改变【默认是“一露头”或者“完全出去”,可以基于第二个Options配置项中的threshold来指定规则:threshold:[0|1];0表示:一露头&玩完全出去;1表示:完全出去&出去一点】,都会触发监听器的回调函数callback执行;\
- 在回调函数中可以获取所有监听的DOM和可视窗口的交叉信息;\
- 回调函数:\
- 形参changes:传的实参是一个数组,记录了每一个监听元素和可视窗口的交叉信息(解构赋值使用)\
- boundingClienrRect:记录当前监听元素的getBoundingClientRect获取的值\
- isInteresecting:true/false true代表出现在可视窗口中,false则反之\
- target:储存当前监听的这个DOM元素对象\
- ....
// 创建监听器
let ob = new IntersectionObserver((changes) => {
/*
回调函数执行:
+ 创建监听器、且监听了DOM元素会立即执行一次(连续监听多个DOM只触发一次,但是如果监听是分隔开的,每新监听一个元素都会触发执行一次)
+ 当监听的元素和可视窗口交叉状态改变,也会触发执行「默认是“一露头”或者“完全出去”,会触发;当然可以基于第二个Options配置项中的threshold来指定规则;」
+ threshold: [0] 一露头&完全出去
+ ...
+ threshold: [1] 完全出现&出去一点
----
changes:是一个数组,记录了每一个监听元素和可视窗口的交叉信息
+ boundingClientRect:记录当前监听元素的getBoundingClientRect获取的值
+ isIntersecting:true/false true代表出现在可视窗口中,false则反之
+ target:存储当前监听的这个DOM元素对象
+ ...
*/
console.log(changes);
}, { threshold: [1] });
// 监听某个DOM元素和可视窗口的交叉状态改变;unobserve移除监听;
ob.observe(box1);
ob.observe(box2);
五、图片懒加载(优化必做)
思路:
- 最开始加载页面的时候,IMG的SRC不赋值(目的:不会加载真实照片,把真实图片的地址赋值给IMG的自定义属性,方便后期想要加载真实图片时候获取)
- 如果SRC不赋值或者加载图片是错误的,会显示“碎图”,导致样式不美观,所以我们在最开始的时候让IMG隐藏(可以通过display、也可以设置透明度opacity;透明度改变可以设置过渡效果);
- 给图片所在的盒子设置背景占位图(或者背景颜色),在真实图片没有加载之前,用其占位(盒子宽高事先设置好);
- 啥时候加载?\
- 当页面第一次渲染完(其他资源加载完成,例如:window.onload)\
- 把出现在当前可是窗口内的图片进行加载
- 如何加载?\
- 获取图片的自定义属性值,拿到真实图片地址\
- 给图片的SRC赋值真实地址:如果图片可以正常加载成功,则让IMG显示
// 图片懒加载
const lazy = function lazy() {
let trueImg = imgBox.getAttribute('data-img');
imgBox.src = trueImg;
imgBox.onload = () => {
imgBox.style.opacity = 1;
};
// 设置自定义属性:当前图片已经处理过延迟加载了
picBox.isLoad = true;
};
// 计算何时加载{完全出现}
const computed = function computed() {
// 如果之前已经处理过,则无需再次处理了
if (picBox.isLoad) return;
let C = HTML.clientHeight,
{ top: B, bottom: A } = picBox.getBoundingClientRect();
if (A <= C && B >= 0) {
lazy();
}
};
// 第一次其它资源加载完计算一次 & 页面滚动中随时计算
// + scroll事件会在浏览器滚动条滚动中进行触发,并且按照浏览器最快反应时间(一般5~7ms)的频率进行触发!!例如:我们滚动100ms,按照5ms触发一次,一共触发20次!! “触发频率太快了,造成了没必要的计算和性能消耗”
// + 此时我们需要“降低”触发频率「我们不是降低浏览器的触发频率,而是把computed函数执行的频率降下来」,我们此操作称之为“函数节流”!!
window.onload = computed;
window.onscroll = utils.throttle(computed, 100); //throttle:utils的节流函数
const picBox = document.querySelector('.pic-box'),
imgBox = picBox.querySelector('img');
// 延迟加载
const lazy = () => {
let trueImg = imgBox.getAttribute('data-img');
imgBox.src = trueImg;
imgBox.onload = () => {
imgBox.style.opacity = 1;
};
};
// 创建监听器监听pic-box,控制延迟加载:无需再进行复杂运算、也无需考虑函数节流...
const ob = new IntersectionObserver(([item]) => {
if (item.isIntersecting) {
// 完全出现在视口中:延迟加载
lazy();
// 处理过的需要移除监听
ob.unobserve(item.target);
}
}, { threshold: [1] });
ob.observe(picBox);