对开发者而言,代码结构的可读性更强,对于标题,段落,列表等结构一目了然,方便快速定位和维护
对于搜索引擎而言,搜索引擎通过爬虫获取到html结果,对于使用语义化标签的结果能更加方便的分清内容结构和关系,比如标题和段落,方便搜索引擎读懂结果。
inline: span img input button
等
block/table: div h1 h2... table ul ol p
等
#div1 {
width: 100px;
padding: 10px;
border: 1px solid #ccc;
margin: 10px;
}
请问div1
的offsetWidth是多大? 答案100+20+2=122
$$
offsetWidth = width+padding+border
$$
如果要让offsetWidth变成100,只需要box-sizing:border-box
相邻元素的margin-top和margin-bottom会重叠,最终距离是取两者较大者
Block Format Context 块级格式化上下文
一块独立渲染区域,内部元素的渲染不会影响边界以外的元素
形成BFC的条件:
常见应用: 清除浮动
圣杯布局和双飞翼布局的目的:
圣杯布局: header + 中间三栏 + footer
双飞翼布局: 三栏布局,中间自适应
圣杯布局的要点
<div class="header">header</div>
<div class="container">
<!--要先写中间,因为要让中间的元素先渲染 -->
<div class="center column">center</div>
<div class="left column">left</div>
<div class="right column">right</div>
</div>
<div class="footer">footer</div>
header和footer没有什么难点,主要是中间container如果不BFC,可以让footer用BFC清除浮动
body {
min-width: 600px; /*防止太窄使得三列换行 min-width = 2*left + right */
}
.column {
float: left;
height: 200px;
text-align: center;
line-height: 200px;
}
.container {
padding-left: 200px; /* 左右使用padding预留空间 */
padding-right: 150px;
}
.center {
width: 100%;
background-color: green;
}
.left {
background-color: red;
width: 200px;
margin-left: -100%; /*把left挤到center前面去*/
position: relative;
left: -200px; /*相对位置,把自己推到父容器的padding中*/
}
.right {
width:150px;
background-color: blue;
margin-right: -150px;/*将右边元素挤到左边来,因为它右边已经没有元素了,达成目的是从外部来看,它自身的宽度width+marigin=0了,可以放到上一行中*/
}
.footer {
clear: both;
}
双飞翼布局
<div class="center column">this is center</div>
<div class="left column">this is left</div>
<div class="right column">this is right</div>
.column {
height: 200px;
float: left;
line-height:200px;
text-align: center;
}
.center {
background-color: grey;
width: 100%;
}
/* 这里得有个wrap通过margin来预留空间,但是父级center没有预留空间 */
.center-wrap {
margin: 0 190px 0 190px;
}
.left {
background-color: aqua;
width: 190px;
margin-left:-100%;
}
.right {
width: 190px;
background-color: bisque;
margin-left: -190px; /*这里是因为center没有往右边预留空间,如果.right宽度为0还是会被放到center的后面,
现在是要right盖在center-wrap的margin-right的区域上,因此直接让right往左边移动挤进center上就行了*/
}
<div class="container">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
.container {
width: 300px;
height: 300px;
border-radius: 20px;
border: 1px solid #ccc;
padding: 50px;
display: flex;
align-items: center;
justify-content: space-between;
}
.circle{
width: 50px;
height: 50px;
border: 1px solid #ccc;
background-color: blue;
border-radius: 50%;
}
.circle:first-child {
align-self: flex-start;
}
.circle:last-child {
align-self: flex-end;
}
flex常用的属性要记住:
body{
font-size:20px;
line-height: 200%;
}
p {
font-size: 16px;
}
<body>
<p>
p标签
</p>
</body>
p标签的line-height是多少? 40px;
line-height继承规则:
比如30px
, 就继承该值2/1.5
,就继承该比例200%
,则继承计算之后的值(父值计算完之后给子)媒体查询+rem
rem的弊端
“阶梯”性: 媒体查询的弊端,在一个范围内rem代表的尺寸是固定的,不会随着屏幕的变化而变化,无法实现一种真正意义上的完全的响应式
网页视口尺寸
vh:网页视口高度的1/100
vw:网页视口宽度的1/100
vmax: 取两者(vh,vw)最大值
vmin: 同理
识别所有值类型(undefined,string,number,boolean,symbol) 和 function, object
值类型一般存在栈中,如果a,b都是值类型,b=a实际上是将a的数据复制了一份给b
需要考虑很多情况,这里有个比较好写的版本
// 可继续遍历类型
const mapTag = "[object Map]";
const setTag = "[object Set]";
const objectTag = "[object Object]";
const arrTag = "[object Array]";
const deepType = [mapTag, setTag, objectTag, arrTag];
function getType(obj) {
return Object.prototype.toString.call(obj);
}
function getCloneObj(target, typeTag) {
if(typeTag === objectTag) {
const allDesc = Object.getOwnPropertyDescriptors(target); // 拷贝属性描述符
return Object.create(Object.getPrototypeOf(target),allDesc); // 拷贝原型
}else{
return new target.constructor(target);
}
}
function deepClone(target, hash = new WeakMap()) {
if (typeof target !== 'object') {
return target;
}
if (hash.has(target)) {
return hash.get(target)
}
const typeTag = getType(target);
const cloneObj = getCloneObj(target, typeTag);
hash.set(target, cloneObj);
if (deepType.includes(typeTag)) {
if (typeTag === mapTag) {
target.forEach((value, key) => {
cloneObj.set(key, deepClone(value, hash));
})
}
if (typeTag === setTag) {
target.forEach(value => {
cloneObj.add(deepClone(value, hash))
})
}
if ([arrTag, objectTag].includes(typeTag)) {
for (const key in target) {
// 只拷贝自身属性,父类型继承的Property不拷贝
if (target.hasOwnProperty(key)) {
cloneObj[key] = deepClone(target[key], hash);
}
}
}
}
return cloneObj;
}
引入的知识点是instanceof的使用规则,比如判断a instanceof Array
,就是看Array.prototype
是否存在于a.__proto__
的原型链上。
class Person {
constructor(){}
}
typeof Person; // function 类本质上就是一个(构造)函数
const p = new Person();
p.__proto__ === Person.prototype; // true
Function.prototype.bind = function() {
var args = Array.prototype.slice.call(arguments);
var context = args.splice(0,1)[0];
var fn = this;
var res = function() {
let rest = Array.prototype.slice.call(arguments);
return fn.apply(context, args.concat(rest));
}
// 保留原型
if(this.prototype) {
res.prototype = this.prototype;
}
return res;
}
注意一点:自由变量的查找规则
var a = 100;
function logA() {
console.log(a);
}
function t(fn) {
var a = 200;
fn();
}
t(logA); // 打印的是100
自由变量:当前函数作用域内不存在的变量
自由变量的查找规则:会在声明函数的作用域链上(或者叫上一级)查找,和调用函数的作用域没有关系
**event loop是一个执行模型,**在不同的地方有不同的实现。浏览器中的Event Loop是html5规范中明确定义的。
上图的将浏览器的Even loop完整的描述出来了
宏任务macrotask,也叫task。一些异步任务的回调会依次进入macrotask queue,等待后续被调用
微任务microtask,也叫jobs。另一些异步任务的回调会依次进入microtask queue,等待后续被调用
const p = document.querySelector('p');
p.style.width = '100px'; // property的形式
p.setAttribute('data-name','test'); // attr形式
property修改的是js变量,不会对DOM结构产生影响,attr修改的是标签结点的属性,会显示在DOM结构中
两者都有可能引起DOM的重新渲染
const listNode = document.querySelector('ul');
// 创建一个文档片段,此时还没有插入到DOM树中
const fragment = document.createDocumentFragment();
// 执行插入
for(let x=0;x<10;x++){
const li = document.createElement('li');
li.innerHTML = x + 'tag';
fragment.appendChild(li);
}
// 将片段插入到DOM树中
listNode.appendChild(fragment);
// navigator
navigator.userAgent
//
screen.height
screen.width
// 以 https://www.baidu.com/s?word=abc#123 为例
location.href; // "https://www.baidu.com/s?word=abc#123"
location.protocal; // https
location.host; // www.baidu.com
location.pathname; // /s
location.search; // ?word=abc
location.hash; // #123
function bindEvent(dom, event, selector, cb) {
if (cb == null) {
cb = selector
selector = null
}
if (dom) {
dom.addEventListener(event, (e) => {
let target
if (selector) {
// 代理事件
target = e.target
if (target.matches(selector)) {
cb.call(target, e)
}
} else {
// 不需要代理
cb(e);
}
})
}
基于DOM树结构,指事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播至最不具体的那个节点(文档)
事件代理
var xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:5500/src/%E9%9D%A2%E8%AF%95%E9%A2%98/');
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
console.log(xhr.responseText)
}
}
}
xhr.send()
document.cookie='a=100;b=2';
cookie的劣势:
HTML5专为存储而设计,最大可存5M
API使用简易:setItem / getItem
不会随http请求发送
区别:
1xx: 服务端已经收到请求,还未响应
2xx: 服务端成功响应
3xx: 重定向相关(配合response header-Location使用,浏览器自动解析跳转), 304和缓存相关
4xx: 客户端请求错误
5xx: 服务端错误
Accept:可接收的数据格式
Accept-Encoding:浏览器可接收的压缩算法,如gzip
Accept-Language:语言
Connection:keep-alive 一次TCP连接重复使用
cookie
Host: 请求的域名
User-Agent
Content-type:发送数据的格式 如application/json
Content-type:返回的数据格式
Content-length:返回数据的大小(字节)
Content-Encoding:返回数据的压缩算法
整个http的缓存可以从两个角度来看待,缓存控制和缓存校验
用于设置缓存策略(缓存控制)
Expires也是类似功能,不过是老版本的浏览器会有的。
是一种服务器端缓存策略,即服务端来判断能不能使用本地缓存的内容。
服务端判断客户端资源,是否和服务端资源一样
一致则返回304,否则返回200和最新的资源
资源标识,在response header中,有两种
<script>
则暂停渲染,优先加载并执行js代码,完成再继续渲染window.onload=function(){
/*
页面的全部资源加载完才会执行,包括图片和视频
*/
}
document.addEventListener('DOMContentLoaded',function(){
/*
DOM渲染完即可执行,此时图片等资源可能还没有加载完
*/
})
节流是表示同一事件频繁触发,在一段时间内只执行一次回调(比如鼠标移动,wait时间为500ms,则每500ms会触发一次事件回调)
function throttle(fn,wait){
var timer;
return function() {
var context = this;
var args = arguments;
if(!timer){
// 如果计时器不存在,就设置一个计时器,计时结束后会自己置空计时器
timer = setTimeout(function() {
fn.apply(context,args);
clearTimeout(timer);
timer = null;
},wait);
}
}
}
防抖是表示同一事件频繁触发,在触发完之后等待一段时间才会执行一次回调,如果一段时间内又触发了,那就以新的为准,再等一段时间(比如鼠标移动,wait时间为500ms,500ms内一直在移动,那么回调一直等,直到鼠标停止后500ms后,回调执行)
function debounce(fn, wait) {
var timer
return function () {
var context = this
var args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
fn.apply(context, args);
}, wait)
}
}
场景:
在一个博客网站中,我发表一篇博客,其中嵌入script脚本,脚本内容是获取cookie并且发送到指定的服务器上(服务器配合跨域)
博客发布后,有人查看,会执行script脚本,我轻松收割访问者的cookie
防范:
<
变为<l
场景:
Bob通过给银行网站发送请求http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 将钱转给bob2,请求发送给后端服务器后,服务器会验证session,检查合法且该session的用户Bob已经成功登陆
黑客做了一个钓鱼网站,在网站中放入img src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory“,诱导Bob访问网站,然后执行了该请求,服务端验证Bob的session未过期且已经登陆,则转账成功
防范:
主要思路是在请求中加入无法伪造的验证信息,CSRF之所以能成功,是因为关键的验证信息都存在于cookie中,而黑客可以在不知道这个验证信息的情况下伪造用户自己发出的请求。
可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue
。 而对于 POST 请求来说,要在 form 的最后加上 <input type="hidden" name="csrftoken" value="tokenvalue"/>
,这样就把 token 以参数的形式加入请求了
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。