1.CSS盒子模型
- 1.传统盒子模型
- width/height:内容的宽高
- padding:内填充
- border:边框
- 盒子本身的大小=内容宽高+内填充padding+边框border
- 2.CSS中新的盒子模型属性:box-sizing:border-box
- width/height:盒子的宽高,不再是内容的宽高了
- width:300px;盒子最后的大小就是300
- 如果我们调整他的border和padding,内容也跟着自动缩放,以保证盒子最后的大小是300px
2.JS盒子模型属性
1.JS中提供了一些对应的API属性方法,可以让我们在JS中后去盒子的相关样式;
2.API(Application Programing Interface):凡是可以被调取使用的都能称作是API
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS盒子模型属性</title>
<link rel="stylesheet" href="reset.min.css">
<style>
.outer {
box-sizing: border-box;
margin: 20px auto;
width: 500px;
height: 500px;
background: lightcoral;
border: 10px solid orangered;
}
.box {
box-sizing: border-box;
margin: 20px auto;
padding: 15px;
width: 300.4px;
height: 300px;
border: 10px solid lightblue;
background: lightcyan;
font-size: 18px;
line-height: 30px;
}
</style>
</head>
<body>
<div class="outer" id="outer">
<div id="box" class="box">
每天都是新的开始,新的开始一定要给与自己更多的快乐和幸福。就算昨天拥有悲伤、
失败和痛苦,这一切都已经留给了昨天,现在就是一个新的起点,打开窗子,让清风吹在脸上,
让视野再宽阔一些。告诉自己,把昨天的悲伤变成今天的快乐,把昨天的失败变成今天的成功,
把昨天的不幸变成今天的幸福,如果昨天快乐,昨天幸福,昨天成功,
那么,为了同样的目标,今天也还是一个新的起点。
</div>
</div>
</body>
</html>
- 1.clientWidth:可是窗口的宽度
- 2.clientHeight:可是窗口的高度
1.内容+padding;内容溢出没有影响
console.log(box.clientWidth);//=>280
console.log(box.clientHeight);//=>280
2.获取浏览器可视窗口(浏览器一屏幕的宽高)
//=>document.documentElement 获取HTML元素对象
console.log(document.documentElement.clientWidth);
console.log(document.documentElement.clientHeight);
- 3.clientLeft:左边框的宽度
- 4.clientTop:上边框的高度
获取 左边框和上边框的大小(没有获取有边框和下边框的盒子模型)
console.log(box.clientLeft);//=>10
console.log(box.clientTop);//=>10
- 5.offsetWidth:就是clientWidth基础上加上了左右边框(算上边框的宽度)
- 6.offsetHeight:就是clientHeight基础上加上了上下边框(算上边框的高度)
只是在client的基础上加上了边框的大小,所以内容是否溢出对其也没有影响
console.log(box.offsetWidth);
console.log(box.offsetHeight);
- 7.offsetLeft:获取距离左偏移
- 8.offsetTop:获取距离上偏移
- 9.offsetParent:获取距离其父元素参照物
1.父级参照物不是父级元素 offsetParent
2.距离其父参照物的内边框(从当前元素外边框开始,到父参照物的内边框)
3.父参照物指的是:同一个平面中最外层的元素是内层所有元素的父参照物
4.默认情况下,所有的元素都在同一个文档流(同一个平面)中,这样他们的父参照物都是BODY,所获取的偏移也都是距离BODY的
5.但是一旦某些元素设置了position(relative/absolute/fixed)就会形成一个新的平面,那么其内部元素的父参照物就不在是BODY,而是当前元素本身
console.log(outer.offsetParent); //=>BODY
console.log(box.offsetParent); //=>BODY
outer.style.position = 'relative';
console.log(outer.offsetParent); //=>BODY
console.log(box.offsetParent); //=>OUTER
- 10.scrollWidth:包括溢出的宽度
- 11.scrollHeight:包括移除的高度
1.在内容没有溢出的情况下, scrollWidth/scrollHeight与clientWidth/clientHeight结果是一样的
2.在内容有溢出的情况下,它的结果包含了溢出内容宽高(但是这个值是一个约等于的值,不完全准确的:在不同浏览器中,因为对内容渲染机制的差异,结果是不一样的,而且我们设置的overflow的值也对最后的结果有影响)
console.log(box.scrollWidth);
console.log(box.scrollHeight);
3.获取当前页面真实的宽度和高度,包括那部分溢出的内容
console.log(document.documentElement.scrollHeight);
console.log(document.documentElement.scrollWidth);
- 12.scrollLeft:横向滚动条卷去的宽度
- 13.scrollTop:竖向滚动条卷去的高度
scrollLeft/scrollTop是13个盒子模型属性中唯二可以修改的属性(其余都是只读的,只有这两个是可读写的),通过修改对应的值,可以控制滚动条的滚动
最小值0 最大值 scrollHeight-clientHeight
//1.在内容高度溢出的时候,不知道溢出多少,给盒子多大,可以用这个api测试一下内容的高度是多少
console.log(box.scrollTop);
//2.在内容横向溢出的时候,不知道溢出多少,给盒子多大,可以用这个api测试一下内容的高度是多少
console.log(box.scrollLeft);
3.案例:回到顶部
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>回到顶部</title>
<link rel="stylesheet" href="reset.min.css">
<style>
html,
body {
height: 1000%;
background: -webkit-linear-gradient(top left, red, green, blue, orange, pink);
}
#GOTO {
position: fixed;
right: 0;
bottom: 100px;
width: 80px;
height: 50px;
line-height: 50px;
text-align: center;
display: none;
}
</style>
</head>
<body>
<button id="GOTO">回到顶部</button>
<script>
// 开始不显示回到顶部按钮,当随着滚动条滚动,卷去的高度超过了一屏幕的大小,我们再显示这个按钮
window.onscroll = function () {
// 不论我们基于什么方式,控制了滚动条的滚动,都会触发这个事件,让绑定的方法执行
let sT = document.documentElement.scrollTop,
cH = document.documentElement.clientHeight;
if (sT >= cH) {
GOTO.style.display = 'block';
} else {
GOTO.style.display = 'none';
}
};
GOTO.onclick = function () {
document.documentElement.scrollTop = 0;
};
</script>
</body>
</html>
4.封装样式
1.从主体获取当前元素的左偏移和上偏移
兼容问题:在标准的IE8浏览器中,偏移值是当前元素外边框距离父参照物的外边框的距离(其它浏览器都是距离父参照物内边框),所以我们计算偏移值的时候,如果是IE8浏览器,则边框不需要在加了
function offset(element) {
// 1.首先获取当前元素的父参照物和其距离父参照物的偏移
let parent = element.offsetParent,
top = element.offsetTop,
left = element.offsetLeft;
// 2.循环依次向上查找父参照物(一直到找不到为止)
while (parent) {
// 加上父参照物的边框(非IE8浏览器中才加)
if (!/MSIE 8/.test(navigator.userAgent)) {
left += parent.clientLeft;
top += parent.clientTop;
}
// 加上父参照物的偏移值
left += parent.offsetLeft;
top += parent.offsetTop;
// 继续向上查找
parent = parent.offsetParent;
}
// 3.把查找的结果返回
return {
// top:top
top,
left
};
}
// 控制台:navigator.userAgent获取当前浏览器的版本信息
// 使用:控制台
/*outer.style.position='relative';
box.offsetLeft
offset(box)*/
2.获取样式
//element 获取的元素下的
//attr:要获取样式的属性名
//例如:控制台 getCss(box.width) getCss(box.Height)
function getCss(element, attr) {
// 优化:如果我们获取的结果符合 "数字+单位" 的格式,我们默认就把单位去掉,
//变为纯数字;如果获取的是“纯数字的字符串”,我们也把其转换为数字;
//如果是组合值或者非数字的,则不进行任何的处理;
let value = window.getComputedStyle(element)[attr],
reg = /^\d+(px|rem|em)?$/i;
if (reg.test(value)) {
value = parseFloat(value);
}
return value;
}
3.给元素设置样式
// JS中给元素设置样式一般也有几种方法:
// =>设置行内样式 元素.style.xxx=xxx | 元素.style.cssText='xxx'
// =>设置样式类 元素.className=xxx
function setCss(element, attr, value) {
// 优化:如果传递的是opacity,在IE低版本中用的filter设置样式
if (attr === "opacity") {
element['style']['opacity'] = value;
element['style']['filter'] = `alpha(opacity=${value*100})`;
return;
}
// 优化:对于某些特定样式(例如:width/height/margin
// /padding/marginTop.../paddingTop.../left/top/bottom/right...)
//如果传递的value没有带单位的(纯数字)我们手动默认给他加上PX单位
let reg = /^(width|height|margin|padding)?(top|left|bottom|right)?$/i;
if (reg.test(attr)) {
// 只有人家value没有传单位,我们才自己加单位(只有传递的是有效数字,我们才设置单位)
if (!isNaN(value)) {
value += 'px';
}
}
element['style'][attr] = value;
}
/*
//使用:控制台
例如:
setCss(box,'width','200px')
setCss(box,'height','auto')
*/
4.批量设置样式
// options是一个对象,包含了我们需要批量设置样式的属性名和属性值
function setGroupCss(element, options) {
// 循环传递的样式对象,分别调取setCss设置样式即可
for (let key in options) {
if (!options.hasOwnProperty(key)) break;
setCss(element, key, options[key]);
}
}
/*
使用:控制台
setGroupCss(box, {
width: 100,
height: 100
}); */
5.实现批量设置
- 1.如果传递的是三个参数则为单一设置样式
- 2.如果传递的是两个参数
- 第二个参数是个对象:则为批量设置样式
- 第二个参数是个字符串:则为获取样式
function css(element) {
let len = arguments.length,
attr = arguments[1],
value = arguments[2];
if (len >= 3) {
// 单一设置样式
setCss(element, attr, value);
return;
}
if (attr !== null && typeof attr === "object") {
// 批量设置
setGroupCss(element, attr);
return;
}
// 获取样式
return getCss(element, attr);
}
使用:控制台
例如:
//1.获取box盒子的宽度
css(box,'width')
//2.批量设置样式
css(box,{
color:'red',
opactity:0.5
})
//3.单一设置样式
css(box,'background','orang')
5.样式库
utils.js
/*
* UTILS中集合了我们以后项目中经常会使用的方法
*/
let utils = (function () {
function getCss(element, attr) {
let value = window.getComputedStyle(element)[attr],
reg = /^\d+(px|rem|em)?$/i;
if (reg.test(value)) {
value = parseFloat(value);
}
return value;
}
function setCss(element, attr, value) {
if (attr === "opacity") {
element['style']['opacity'] = value;
element['style']['filter'] = `alpha(opacity=${value*100})`;
return;
}
let reg = /^(width|height|margin|padding)?(top|left|bottom|right)?$/i;
if (reg.test(attr)) {
if (!isNaN(value)) {
value += 'px';
}
}
element['style'][attr] = value;
}
function setGroupCss(element, options) {
for (let key in options) {
if (!options.hasOwnProperty(key)) break;
setCss(element, key, options[key]);
}
}
function css(element) {
let len = arguments.length,
attr = arguments[1],
value = arguments[2];
if (len >= 3) {
// 单一设置样式
setCss(element, attr, value);
return;
}
if (attr !== null && typeof attr === "object") {
// 批量设置
setGroupCss(element, attr);
return;
}
// 获取样式
return getCss(element, attr);
}
function offset(element) {
let parent = element.offsetParent,
top = element.offsetTop,
left = element.offsetLeft;
while (parent) {
if (!/MSIE 8/.test(navigator.userAgent)) {
left += parent.clientLeft;
top += parent.clientTop;
}
left += parent.offsetLeft;
top += parent.offsetTop;
parent = parent.offsetParent;
}
return {
top,
left
};
}
// window['_css'] = css;
// window['_offset'] = offset;
return {
css,
offset
};
})();
6.图片延迟加载(第一屏)图片的懒加载
为什么需要做图片的懒加载 : 第一次加载页面的时候,如果请求真实图片资源,会减缓页面的渲染速度,为了提高首次打开的体验度,我们最开始不加载真实的图片资源,只有当第一次页面整体都渲染完成后,页面中已经呈现出除图片以外的其它资源后,再去按需加载真实的图片资源渲染=> 进一步优化,最开始只加载首屏需要的图片,等到滚动条滚动的时候,再把当前屏幕中需要渲染的图片加载(避免不必要的资源加载)
- 1.给需要延迟加载的图片设置一个外层盒子,图片在这个盒子中
- 2.我们把图片的SRC设置为空,把后续需要请求的真实图片地址赋值给IMG的自定义属性
- 3.在最开始渲染的样式中:
- 给外层的盒子设置等待加载的背景LOGO(或者背景颜色,如果是背景图片,图片一定要非常的小,最好控制在5KB以内),让盒子起到一个占位的作用
- 让所有的SRC为空的IMG默认是隐藏的
- 4.需要等到页面加载完成后(除图片资源,其它资源都加载完成了 window.onload),在JS中获取真实图片的地址,赋值给其SRC(此时才去加载真实的图片进行渲染)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图片的延迟加载</title>
<!-- IMPORT CSS -->
<link rel="stylesheet" href="css/reset.min.css">
<style>
img:not([src]),
img[src=""] {
display: none
}
.lazyImgBox {
box-sizing: border-box;
width: 300px;
height: 500px;
background: url("images/default.gif") no-repeat center center #DDD;
}
.lazyImgBox img {
width: 100%;
height: 100%;
}
</style>
<script>
/*
不放在末尾,还想在DOM结构加载完成后执行
1. 把SCRIPT改成异步的即可
2. 或者监听事件,在DOM结构加载完成在执行这个代码
=>DOMContentLoaded DOM结构加载完
=>load 所有资源都加载完 (晚于DOMContentLoaded触发的)
*/
/* window.addEventListener('DOMContentLoaded', function () {
let lazyImgBox = document.querySelector('.lazyImgBox');
console.log(lazyImgBox);
}); */
/* window.onload = function () {
let lazyImgBox = document.querySelector('.lazyImgBox');
console.log(lazyImgBox);
}; */
</script>
<!--
async VS defer
async 从服务器获取资源是异步的(此时GUI继续渲染),但是一但资源请求回来,
会立即把请求回来的JS执行(此时会阻断GUI的继续渲染) => 多个asyncJS请求,
谁先回来就先执行谁,没有顺序
defer 从服务器获取资源是异步的,只不过请求回来资源后没有立即执行,
需要等到GUI绘制完成,再去按照顺序把所有请求回来的JS逐一执行
-->
<!-- <script src="js/1.js" async></script> -->
<!-- <script src="js/1.js" defer></script> -->
</head>
<body>
<div class="lazyImgBox">
<img src="" alt="" data-image="images/1.jpg">
</div>
<!-- IMPORT JS -->
<script>
var lazyImgBox = document.querySelector('.lazyImgBox'),
img = lazyImgBox.querySelector('img');
/* window.onload = function () {
// 获取IMG中的真实图片地址 DATA-IMAGE,赋值给它的SRC
let trueSRC = img.getAttribute('data-image');
img.src = trueSRC;
img.removeAttribute('data-image');
}; */
setTimeout(function () {
// 定时器是异步的,它能够触发执行的条件:不仅是到达时间了,
//而且是主线程已经加载完成了(所以可以用它代替window.onload)
// 进一步优化:在IE浏览器中,如果我们加载的图片不存在,
//在图片区域不展示任何图片,会展示一个“×”,不太好看,
//所以我们在赋值SRC之前,一般先自己校验一下地址的真实性
var dataImage = img.getAttribute('data-image');
var tempImg = new Image;
//=>创建一个临时的图片对象(它不会增加到页面中,它就是用来检测地址的合法性的)
tempImg.src = dataImage;
tempImg.onload = function () {
// 当前地址是正确的(tempImg.onload代表图片能正常加载出来)
img.src = dataImage;
};
img.removeAttribute('data-image');
tempImg = null;
}, 1000);
</script>
</body>
</html>
7.图片延迟加载(第二屏)以后图片的懒加载,及渐隐渐显加载出来真真实图片
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图片的延迟加载</title>
<!-- IMPORT CSS -->
<link rel="stylesheet" href="css/reset.min.css">
<style>
/* img:not([src]),
img[src=""] {
display: none
} */
.lazyImgBox {
box-sizing: border-box;
margin: 1000px 0;
width: 300px;
height: 500px;
background: url("images/default.gif") no-repeat center center #DDD;
}
.lazyImgBox img {
width: 100%;
height: 100%;
/* 为了后期图片显示有动画效果 */
opacity: 0;
transition: opacity .3s ease 0s;
}
</style>
</head>
<body>
<div class="lazyImgBox">
<img src="" alt="" data-image="images/1.jpg">
</div>
<!-- IMPORT JS -->
<script src="js/utils.js"></script>
<script>
let lazyImgBox = document.querySelector('.lazyImgBox'),
img = lazyImgBox.querySelector('img'),
HTML = document.documentElement;
// B:图片所在盒子底边距离BODY顶端的距离
// cH:浏览器可视窗口一屏幕的高度
// A:浏览器底边框距离BODY顶端的距离
let B = utils.offset(lazyImgBox).top + lazyImgBox.offsetHeight,
cH = HTML.clientHeight,
A = 0;
window.onscroll = function () {
if (img.isLoad) return; //=>已经处理过了,则其余的都不在处理
A = cH + HTML.scrollTop;
// 延迟加载的条件:图片完全出现在可视窗口中(B<=A)
if (B <= A) {
// 问题:当我们把图片加载出来(或者地址不存在没加载,但是也处理了),
//但是对着浏览器继续滚动,B<=A的条件一直成立,会一直触发延迟加载操作
//=>在第一次处理完成后,后续即使在符合条件我们也不处理了
lazy();
}
};
// 进行图片延迟加载的
function lazy() {
let dataImage = img.getAttribute("data-image"),
tempImage = new Image;
tempImage.src = dataImage;
tempImage.onload = () => {
img.src = dataImage;
// 动画:改变他的OPCITY样式即可
// img.offsetHeight;
//=>先让上面显示的样式渲染(触发一次回流),再让下面透明度的样式改变,
//这样就能看到动画效果了
utils.css(img, 'opacity', 1);
};
img.removeAttribute('data-image');
tempImage = null;
img.isLoad = true;
//=>不论成功还是失败,都标识一下当前图片我已经处理过了
}
</script>
</body>
</html>