JS盒子模型属性
* JS盒子模型属性
* =>在JS中通过相关的属性可以获取(设置)元素的样式信息,这些属性就是盒子模型属性(基本上都是有关于样式的)
*
* client
* top
* left
* width
* height
*
* offset
* top
* left
* width
* height
* parent
*
* scroll
* top
* left
* width
* height
//=>clientTop/Left/Width/Height
//1.clientWidth & clientHeight:获取当前元素可视区域的宽高(内容的宽高+左右/上下PADDING)
//=>和内容是否有溢出无关(和是否设置了OVERFLOW:HIDDEN也无关),就是我们自己设定的内容的宽高+PADDING
//=>不设置高度则按照撑开的计算
//=>获取当前页面一屏幕(可视区域)的宽度和高度
// document.documentElement.clientWidth || document.body.clientWidth
// document.documentElement.clientHeight || document.body.clientHeight
//2.clientTop & clientLeft:获取(上/左)边框的宽度
//3.offsetWidth & offsetHeight:在client的基础上加上border(和内容是否溢出也没有关系)
//4、scrollWidth & scrollHeight:真实内容的宽高(不一定是自己设定的值,因为可能会存在内容溢出,有内容溢出的情况下,需要把溢出的内容也算上)+ 左/上PADDING,而且是一个约等于的值 (没有内容溢出和CLIENT一样)
//=>在不同浏览器中,或者是否设置了OVERFLOW:HIDDEN都会对最后的结果产生影响,所以这个值仅仅做参考,属于约等于的值
//=>获取当前页面的真实宽高(包含溢出的部分)
// document.documentElement.scrollWidth || document.body.scrollWidth
// document.documentElement.scrollHeight || document.body.scrollHeight
获取元素的具体样式
//=>通过JS盒模型属性获取值的特点
//1.获取的都是数字不带单位
//2.获取的都是整数,不会出现小数(一般都会四舍五入,尤其是获取的 偏移量)
//3.获取的结果都是复合样式值(好几个元素的样式组合在一起的值),如果只想获取单一样式值(例如:只想获取PADDING),我们的盒子模型属性就操作不了了(这不能说没有用,真实项目中,有时候我们就是需要获取组合的值来完成一些操作)
//===========[获取元素具体的某个样式值]
//1.[元素].style.xxx 操作获取
//> 只能获取所有写在元素行内上的样式(不写在行内上,不管你写没写都获取不到,真实项目中我们很少会把样式写在行内上)
//=>outer.style.width =>'' (width是写在样式表中的)
//2.获取当前元素所有经过浏览器计算的样式
//> 经过计算的样式:只要当前元素可以在页面中呈现(或者浏览器渲染它了),那么它的样式都是被计算过的
//=>不管当前样式写在哪
//=>不管你是否写了(浏览器会给元素设置一些默认样式)
/*
* 标准浏览器(IE9+)
* window.getComputedStyle([元素],[伪类,一般都写null]) 获取到当前元素所有被浏览器计算过的样式(对象)
*
* IE6~8
* [元素].currentStyle 获取经过计算的样式
*/
/*
* getCss:获取当前元素某一个样式属性值
*
* @param
* curEle[object]:当前要操作的元素
* attr[string]:当前要获取的样式属性名
*
* @return
* 获取的样式属性值
*/
let getCss = function getCss(curEle, attr) {
if ('getComputedStyle' in window) {
let val = window.getComputedStyle(curEle, null)[attr];
//=>把获取的结果去除单位(不是所有的值都能去单位的,例如:display\一些复合值都去不掉单位),只有符合 数字+单位 这种模式的结果才能基于PARSE-FLOAT去单位
let reg = /^-?\d+(\.\d+)?(px|rem|em|pt)?$/i;
reg.test(val) ? val = parseFloat(val) : null;
return val;
}
//=>throw new SyntaxError:抛出一个错误(语法错误),让浏览器崩溃,不在继续执行JS
throw new SyntaxError('您的浏览器版本过低,请升级到最新版本,谢谢配合!!');
};
console.log(getCss(outer, 'width'));
封装SET-CSS设置元素的样式
//=>设置当前元素的某一个具体样式的属性值
//JS中给元素设置样式只有两种
//1.设置元素的样式类名(前提:样式类及对应的样式已经处理完成)
//2.通过行内样式设置 xxx.style.xxx=xxx
let setCss = function (curEle, attr, value) {
/*
* 细节处理
* 1.如果需要考虑IE6~8兼容,透明度这个样式在低版本浏览器中不是使用opacity,而是filter(我们两套都要设置)
* 2.如果传递进来的VALUE值没有带单位,我们根据情况设置PX单位
* ->某些样式属性才会加单位:WIDTH/HEIGHT/PADDING(LEFT...)/MARGIN(LEFT...)/FONT-SIZE/TOP/LEFT/BOTTOM/RIGHT...
* ->用户自己传递的VALUE值中是没有单位的
*/
if (attr === 'opacity') {
curEle.style.opacity = value;
curEle.style.filter = `alpha(opacity=${value * 100})`;
return;
}
if (!isNaN(value)) {
//=>IS-NaN检测的结果是FALSE:说明VALUE是纯数字没单位
let reg = /^(width|height|fontSize|((margin|padding)?(top|left|right|bottom)?))$/i;
reg.test(attr) ? value += 'px' : null;
}
curEle['style'][attr] = value;
};
封装SET-GROUP-CSS批量设置元素的样式
//=>给元素批量设置样式
let setGroupCss = function (curEle, options = {}) {
//=>遍历传递的OPTIONS,有多少键值对,就循环多少次,每一次都调取SET-CSS方法逐一设置即可
for (let attr in options) {
if (!options.hasOwnProperty(attr)) break;
//=>options:传递进来的需要修改的样式对象(集合)
//=>attr:每一次遍历到的集合中的某一项(要操作的样式属性名)
//=>options[attr]:传递的要操作的样式属性值
setCss(curEle, attr, options[attr]);
}
};
setGroupCss(outer, {
width: 400,
height: 400,
padding: 30
});
FOR-IN循环
/*
* FOR-IN循环
* =>遍历一个对象中的键值对的,有多少组键值对,我们就遍历多少次
*/
// let obj = {name: 'xxx', age: 27, 0: 0, sex: 0, score: 100, 1: 1};
/*for (let key in obj) {
// console.log(key);//=>KEY存储的是每一次循环获取的属性名
// console.log(obj[key]);//=>每一次循环基于KEY获取属性值
// if(key==='age'){
// break; 也支持BREAK和CONTINUE等关键词
// }
// console.log(key);
}*/
/*//=>FOR-IN遍历的时候有自己的顺序:先遍历数字属性名(按照小->大),再遍历字符串属性名(按照书写顺序)
for (let attr in obj) {
console.log(attr);//=>0 1 name age sex score
}*/
/*//=>obj.__proto__===Object.prototype : obj是Object这个类的一个实例
//=>大括号中的是OBJ的私有属性,Object.prototype上的是OBJ公有属性
Object.prototype.bbbb = 1000;
for (let key in obj) {
//=>FOR-IN循环只遍历当前对象可枚举(可遍历)的属性
//1.对象的私有属性(自己写的)是可枚举的
//2.浏览器内置的属性一般都是不可枚举的
//3.自己在类的原型上设置的属性也是可枚举的,FOR-IN循环的时候也会被遍历出来(一般情况下我们是不想遍历到原型上的公有属性的)
if (obj.hasOwnProperty(key)) {//=>一般使用FOR-IN在遍历对象的时候,我们加一个私有属性的验证,只有是私有的属性,我们才做操作
console.log(key);
}
}*/
封装UTILS库实现CSS方法统一操作样式
//=>CSS:集合GET/SET/SET-GROUP为一体的方法
let css = function (...arg) {
//=>ARG:传递的实参集合
let len = arg.length;
if (len >= 3) {
//=>单一设置:SET-CSS
// arg=[outer, 'width', 500];
// setCss(outer, 'width', 500);
// setCss.apply(null,arg);
setCss(...arg);
return;
}
if (len === 2 && typeof arg[1] === 'object' && arg[1] !== null) {
//=>传递两个参数,第二个参数是一个对象(不是NULL),说明想要操作的是批量设置
setGroupCss(...arg);
return;
}
//=>剩下的代表获取样式
return getCss(...arg);
};
let css = function (...arg) {
let len = arg.length,
fn = getCss;
len >= 3 ? fn = setCss : null;
len === 2 && (arg[1] instanceof Object) ? fn = setGroupCss : null;
return fn(...arg);
};
公共方法库
//=>公共方法库:项目中常用的一些方法,我们都封装到这里
let utils = (function () {
//=>获取元素的样式
let getCss = function (curEle, attr) {
if (typeof window.getComputedStyle === 'undefined') {
return;
}
let val = window.getComputedStyle(curEle, null)[attr],
reg = /^-?\d+(\.\d+)?(px|rem|em|pt)?$/i;
reg.test(val) ? val = parseFloat(val) : null;
return val;
};
//=>设置元素样式
let setCss = function (curEle, attr, value) {
if (attr === 'opacity') {
curEle.style.opacity = value;
curEle.style.filter = `alpha(opacity=${value * 100})`;
return;
}
if (!isNaN(value)) {
let reg = /^(width|height|fontSize|((margin|padding)?(top|left|right|bottom)?))$/i;
reg.test(attr) ? value += 'px' : null;
}
curEle['style'][attr] = value;
};
//=>批量设置元素样式
let setGroupCss = function (curEle, options = {}) {
for (let attr in options) {
if (!options.hasOwnProperty(attr)) break;
setCss(curEle, attr, options[attr]);
}
};
//=>CSS操作汇总
let css = function (...arg) {
let len = arg.length,
fn = getCss;
len >= 3 ? fn = setCss : null;
len === 2 && (arg[1] instanceof Object) ? fn = setGroupCss : null;
return fn(...arg);
};
//=>offset:获取当前元素距离BODY的偏移(左偏移和上偏移)
let offset = function (curEle) {
//1.先获取当前元素本身的左/上偏移
let curLeft = curEle.offsetLeft,
curTop = curEle.offsetTop,
p = curEle.offsetParent;
//2.累加父参照物的边框和偏移(一直向上找,找到BODY为止,每当找到一个父参照物都把它的边框和偏移累加起来,根据元素不一样,具体找几次也不知道)
//TAG-NAME获取当前元素的标签名(大写的)
while (p.tagName !== 'BODY') {//=>当找到的父参照物是BODY结束查找和累加操作
//3.把找到的父参照物的边框和偏移值累加起来
curLeft += p.clientLeft;
curLeft += p.offsetLeft;
curTop += p.clientTop;
curTop += p.offsetTop;
p = p.offsetParent;//=>基于当前找到的父参照物继续向上查找
}
return {
top: curTop,
left: curLeft
};
};
//=>操作浏览器盒子模型属性的
let winHandle = function (attr, value) {
if (typeof value !== 'undefined') {
//=>设置盒子模型属性值:SCROLL-TOP/LEFT
document.documentElement[attr] = value;
document.body[attr] = value;
return;
}
return document.documentElement[attr] || document.body[attr];
};
return {
css, //=>在ES6中直接这样写相当于 css:css
offset,
winHandle
}
})();
盒子偏移量和OFFET
* offsetParent:当前盒子的父级参照物
//=>“参照物”:同一个平面中,元素的父级参照物和结构没有必然联系,默认他们的父级参照物都是BODY(当前平面最外层的盒子) BODY的父级参照物是NULL
// center.offsetParent //=>BODY
// inner.offsetParent //=>BODY
// outer.offsetParent //=>BODY
//=>“参照物可以改变”:构建出不同的平面即可(使用zIndex,但是这个属性只对定位有作用),所以改变元素的定位(position:relative/absolute/fixed)可以改变其父级参照物
// utils.css(outer, {
// position: 'relative' //=>把OUTER脱离原有的平面,独立出一个新的平面,后代元素的父级参照物都会以它为参考
// });
// console.log(center.offsetParent);//=>OUTER
// console.log(inner.offsetParent);//=>OUTER
// console.log(outer.offsetParent);//=>BODY
// utils.css(inner, {position: 'absolute'});
// console.log(center.offsetParent);//=>INNER
// console.log(inner.offsetParent);//=>OUTER
// console.log(outer.offsetParent);//=>BODY
// console.log(document.body.offsetParent);//=>NULL
* offsetTop / offsetLeft:获取当前盒子距离其父级参照物的偏移量(上偏移/左偏移) 当前盒子的外边框开始~父级参照物的内边框
//=>不管你的父级参照物是谁,我都要获取当前元素距离BODY的偏移量(左偏移和上偏移)
//1.不能修改既定的样式(不能基于POSITION方式改它的参照物了) ===>详情见上方
滚动条卷去的宽度和高度
* scrollTop / scrollLeft:滚动条卷去的宽度或者高度
*
* 最小卷去值:0
* 最大卷去值:真实页面的高度 - 一屏幕的高度 document.documentElement.scrollHeight-document.documentElement.clientHeight
*
* 在JS盒子模型13个属性中,只有scrollTop/scrollLeft是“可读写”属性,其余都是“只读”属性
*
* 操作浏览器的盒子模型属性,我们一般都要写两套,用来兼容各种模式下的浏览器
* ===>详情见上方代码