流月
  • CSS
  • JavaScript
  • Web API
  • TypeScript
  • 框架

    • React
    • Vue
  • 其他

    • 小程序
    • 工程化
    • 性能优化
    • 测试
    • 其他
  • nodejs
  • deno
  • express
  • nginx
  • docker
  • 其他
  • 安全基础
  • 正则表达式
  • 网络基础
  • 设计模式
  • 数据结构与算法
  • LeetCode
  • CodeWars
  • 手写代码
  • Git
  • devops
  • 编码原则
  • 防御编程
  • Chrome
  • Edge
  • Flutter
  • Linux
  • 库
  • 网站
  • 面试
  • 摘抄
  • 方法论
  • 语法
  • 王小波
  • Elon Musk
  • CSS
  • JavaScript
  • Web API
  • TypeScript
  • 框架

    • React
    • Vue
  • 其他

    • 小程序
    • 工程化
    • 性能优化
    • 测试
    • 其他
  • nodejs
  • deno
  • express
  • nginx
  • docker
  • 其他
  • 安全基础
  • 正则表达式
  • 网络基础
  • 设计模式
  • 数据结构与算法
  • LeetCode
  • CodeWars
  • 手写代码
  • Git
  • devops
  • 编码原则
  • 防御编程
  • Chrome
  • Edge
  • Flutter
  • Linux
  • 库
  • 网站
  • 面试
  • 摘抄
  • 方法论
  • 语法
  • 王小波
  • Elon Musk
  • Web API

Web API

浏览器

浏览器内核

  • 内核(渲染引擎)
    • Chrome Webkit
  • JS 引擎
    • Chrome V8

浏览器渲染流程

  • 解析 HTML,构建 DOM Tree
  • 解析 CSS,生成 CSS Tree
  • 当遇到 JS 时,DOM 构建过程会被吊起,需要等待 JS 执行完毕,触发 DOMContentLoaded
  • 合并 DOM Tree 和 CSS Tree,生成 Render Tree
  • 浏览器会将各层的信息发送给 GPU,GPU 会将各层合成(composite),显示在屏幕上

回流(重排)和重绘

  • 回流,负责各元素尺寸、位置的计算
  • 重绘,绘制页面像素信息
    • outline, visibility, color、background-color
  • 重绘不一定造成重排,重排一定会重绘
  • 如何避免重绘或者重排?
    • 使用DocumentFragment,只触发一次重排

render.png

浏览器架构

  • 用户界面
  • 主进程
  • 内核
    • 渲染引擎
    • JS 引擎
    • 事件触发线程
      • 消息队列
        • 微任务
        • 宏任务
    • 网络异步线程
    • 定时器线程

Event Loop 事件循环

理解事件循环对于代码优化很重要,浏览器中 JS 的执行流程和 Node.js 的流程都是基于事件循环的。

JS 并发地接受事件,但会使用一个事件队列按序地处理事件处理程序

进程和线程

  • 进程:资源分配的最小单位,应用程序的执行实例
  • 线程:程序执行的最小单位,线程是进程内的一个独立执行单元,在不同的线程之间是可以共享进程资源的

一个演示

var eventLoop = []
var event

while (true) {
  if (eventLoop.length > 0) {
    event = eventLoop.shift()
    try {
      event()
    } catch(err) {
      reportError(err)
    }
  }
}
  • 有一个 while 循环代表持续运行的循环
  • 循环中的每一轮称为一个 tick
  • 对每个 tick 而言,如果队列中有等待事件,那么就会从队列中摘下一个事件并执行,这个事件就是你的回调函数。 一定要清楚,setTimeout(...)并没有把你的回调函数挂在事件循环队列中。它所做的是设置一个定时器。当定时器到达时,宿主环境会把你的回调函数放在事件循环中。这样在未来的某个时刻的 tick 会摘下并执行这个回调。

如果事件循环中已经由 20 个项目了会怎样?你的回调就会等待。这就解释了 setTimeout(...) 定时器的精度可能不高。只能保证你的回调函数不在指定的时间间隔之前运行,但可能会在那个时刻之后运行,要根据事件队列的状态而定。

不雅阻塞事件循环 一个阻塞的事件处理程序会阻塞任何将被处理的用户输入

  • 浏览器中几乎没有阻塞 API
    • Ajax 有一个同步的 API

同步任务和异步任务

  • 同步任务不进入任务队列,在主线程上排队,执行完一个才能执行下一个
  • 异步任务先在 event table 注册函数,当满足触发条件时,才进入异步任务队列。

宏任务和微任务

  • 把宿主发起的任务称为宏任务
    • script
    • setTimeout
    • setInterval
    • requestAnimationFrame
    • setImmediate(Node)
  • 把 JavaScript 引擎发起的任务称为微任务
    • Promise
    • MutationObserver
    • process.nextTick(Node)

a.png

setTimeout(function() {
	console.log('a')
});

new Promise(function(resolve) {
	console.log('b');

	for(var i =0; i <10000; i++) {
		i ==99 && resolve();
	}
}).then(function() {
	console.log('c')
});

console.log('d');

// b
// d
// c
// a

// 1.首先执行script下的宏任务,遇到setTimeout,将其放入宏任务的队列里。
// 2.遇到Promise,new Promise直接执行,打印b。
// 3.遇到then方法,是微任务,将其放到微任务的队列里。
// 4.遇到console.log('d'),直接打印。
// 5.本轮宏任务执行完毕,查看微任务,发现then方法里的函数,打印c。
// 6.本轮event loop全部完成。
// 7.下一轮循环,先执行宏任务,发现宏任务队列中有一个setTimeout,打印a。

// 作者:童欧巴
// 链接:https://juejin.im/post/5d2036106fb9a07eb15d76e9
// 来源:掘金
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

回流与重绘

  • 触发重排 reflow
  • 触发重绘 repaint

HTML

常用 Meta 标签

  • viewport 控制视口的大小和比例

Viewport

  • width:将布局视口设置为固定的值,比如 375px 或者 device-width(设备宽度)
  • initial-scale:设置页面的初始缩放 width 与 initial-scale 都会初始化布局视口,但浏览器会取其最大值
  • minimum-scale:设置最小的缩小程度
  • maximum-scale:设置最大的放大程度
  • user-scalable:设置为 no 时禁用缩放

强制浏览器使用 WebKit 内核

新增元素

全是语义化的元素

  1. section
  • section标签是有语义化的,不能用它来代替 div 标签
  • 如果希望内容出现在大纲之中,那么最合适的一定是 section 标签。
  1. article
  2. aside
  • 表示与内容关系不大的地方,比如侧边栏、背景描述、引述内容
  1. footer
  • 底部信息,主要描述作者、文档、版权等信息,也不影响大纲
  1. header
  • 头部信息,通常包含标题,它是不会生成大纲的
  1. main

语义化

所谓“语义”就是为了更易读懂,

这要分两部分:

  1. 让人读懂
  • 清晰看出网页的结构
  1. 让机器读懂
  • 对于特殊群体的人群使用屏幕阅读器就会非常的不良好

DOM

Document Object Model

文档对象模型,把 DOM 树解析成一个对象,提供一个接口 API,让你去操作所有的节点。

节点和元素

node.png

  1. textContent
  • 元素中的文本
  1. nodeType // 1是元素
  2. nodeName
  3. nodeValue
  4. parentNode
  5. innerHTML
  6. hidden
  • 当设置为 true 时,执行与 CSS display:none 相同的操作。
  1. value
  • 表单元素支持
  1. type
  • input 元素支持
  1. href
  • a 元素支持

看一下 <input/> 元素,从外向里

  • HTMLInputElement
    • 特定输入的属性
  • HTMLElement 通用 HTML 元素
  • Element
    • 泛型元素方法
  • Node
    • 通用节点属性
  • EventTarget
    • 提供事件系统
  • Object
    • 提供 纯对象 的方法

元素大小

a.pngb.png

  • client size = 内容 + 内边距
    • clientWidth
    • clientHeight
    • 当内边距为 0 时候
      • clientWidth = width
      • clientHeight = height
  • offset size = 内容 + 内边距 + 边框
    • offsetHeight
    • offsetWidth

元素位置

  • clientTop和clientLeft
    • 返回内边距的边缘和边框的外边缘之间的水平和垂直距离,也就是左,上边框宽度
  • offsetLeft/offetTop
    • offsetLeft 表示左外边框到包含元素的左内边框之间的距离
    • 表示该元素的左上角(边框外边框)与已定位的父容器(offsetParent对象)左上角的距离
    • offsetParent
      • 指元素最近的定位(relative,absolute)祖先元素
      • offsetParent = null
        • body 与 html
        • position: fixed 的元素
        • dispaly: none 的元素
  • 滚动条
    • scrollHeight/scrollWidth
      • 内容的高度和宽度
        • 包含 padding
        • 不包含滚动条
    • scrollTop/scrollLeft
      • 元素滚动部分的宽度/高度
      • s.png
  • getBoundingClientRect()
    • .top/left/right/bottom 返回元素的大小及其相对于视口的位置
    • 如何拿到绝对位置
      • 加上 scroll
        • document.body.querySelector('.application-main ').getBoundingClientRect().top + window.scrollY

计算元素到窗口的位置

var offset = (el, type) => {
    var offset = 0
    while(el.offsetParent != null) {
        left += el['offset' + type]
        el = el.offsetParent
       
    }
    return offset
}

元素属性

  • elem.hasAttribute(name) —— 检验是否拥这个特性。
  • elem.getAttribute(name) —— 获取到这个特性值。
  • elem.setAttribute(name, value) —— 设置这个特性值。
  • elem.removeAttribute(name) —— 移除这个特性。

**自定义数据属性 data- ** div.dataset.appid

创建元素

  • document.createElement(tag)
  • document.createTextNode(value) 很少用
  • elem.cloneNode(deep) —— 如果参数 deep==true 将元素及后代子元素进行克隆。

插入元素

  • innerHTML =
    • 使用innerHTML属性与通过多次DOM操作创建节点效率高得多
    • 使用innerHTML是最好先将要替换元素的所有事件处理程序清楚掉
  • outerHTML
  • parent/node
    • appendChild
      • document.body.appendChild(div);

      • parend.appendChild(node)
    • insertBefore
    • removeChild
    • replaceChild
  • current node
    • node.append()
      • node 末尾
    • node.after()
      • node 之后
    • node.prepend()
    • node.before()
    • node.replaceWith()
    • node.remove()
      • 移除node
  • document.write()

createDocumentFragment

  • 不使用 DocumentFragment,批量导入节点列表
<ul id="ul"></ul>

<script>
function getListContent() {
  let result = [];

  for(let i=1; i<=3; i++) {
    let li = document.createElement('li');
    li.append(i);
    result.push(li);
  }

  return result;
}

ul.append(...getListContent()); // append + “...” 操作符 = 一对好朋友!
</script>
var fragment = document.createDocumentFragment();

查找元素

  • document.getElementById()

  • document.getElementsByName()

  • document.getElementsByTagName() 返回一个数组

  • document.getElementsByClassName() 返回一个数组

  • document.querySelector()

  • document.querySelectorAll()

  • elemA.contains(elemB)

  • elem.matches(css)

  • get开头的方法,都只能在document对象上调用

  • 所有的 "getElementsBy*" 方法都会返回 live 集合。这类集合总是可以反映出文档的当前状态而且在文档变化时,可以自动更新。

a.png

querySelectorAll 高级玩法

let selector = 'a[href*="://"]:not([href^="http://internal.com"])';
let links = document.querySelectorAll(selector);

links.forEach(link => link.style.color = 'orange');

遍历元素

b.png

元素层级

  • html
    • 对应 docuemnt
    • 对应 html 标签
    • document.documentElement
  • body
    • document.body
    • document.body 的值可能是 null
      • 比如这个 script 脚本在 head 标签中
  • head
    • document.head

元素遍历

  • 父亲
    • parentElement
  • 兄弟
    • previousElementSibing
    • nextElementSibing
  • 孩子
    • children
    • firstElementChild
    • lastElementChild
    • childElementCount
      • 孩子元素总数

操作样式

动态样式

不能在ie中使用

function loadStyles(url){
  var script = document.createElement('link')
  link.rel = 'stylesheet'
  link.type = 'text/css'
  var head = document.getElementByTagName('head')[0]
  head.appendChild(script)
}

获取样式

getComputedStyle() //ie不支持
document.querySelector("#myDiv").currentStyle; //ie支持

element.style 可读可写 只能获取定义了的 style 样式

window.getComputedStyle(ele, null).getPropertyValue('height') 只读

document.defaultView.getComputedStyle 只读
  • 获取计算后样式
    • getComputedStyle()

设置样式

// 操作 className
document.body.className = 'foo' 会替换掉已有的类名 
var beforeClassName = document.body.className
document.body.className = `${beforeClassName} foo`


var myDiv = document.getElementById('myDiv');
myDiv.style.backgroundColor = 'red';
myDiv.style.width = "100px"

// 应用所有样式
myDiv.style.cssText = "width: 25px; height: 100px"

  • 操作 style 属性
    • 多个属性使用 style.cssText
    • 单个属性
      • elem.style.display = 'none'

  • 操作 className
    • 操作 classList 方法:
      • elem.classList.add/remove("class") —— 添加/移除类。
      • elem.classList.toggle("class") —— 如果类存在就移除,否则添加。
      • elem.classList.contains("class") —— 返回 true/false,检查给定类

事件

事件级别

  • DOM0
  • DOM2
  • DOM3

事件流

事件冒泡 + 事件捕获 + 处于目标阶段

事件处理程序

  1. HTML事件处理程序
    <input type="button" value=""click me" onclick="showMessage()">

  2. DOM0 级
    element[onClick] = handler
    element.onClick = handler

  3. DOM2 级
    var btn = document.getElementById('myBtn')
    btn.addEventListener('click',fn)

  4. IE 事件处理程序
    attachEvent/detachEvent

标准 DOM 事件对象

IE中的事件对象和DOM中的事件对象不一致

currentTarget
stopPropagation() //阻止冒泡
target //实际的目标
type
preventDefault() //阻止默认事件

事件类型

  1. UI事件
  • load
  • error
  • resize
  • scroll
  • select
  1. 焦点事件
  • focus
  • blur
  1. 鼠标事件
  • mousemove
  • mouseleave
  • mouseenter
  • mousedown
  • click
  • dblclick
  1. 键盘事件
  • keydown
  • keypress
  • keyup
  1. html5事件
  • DOMContentLoaded // dom load
  • hashchange事件
  1. touch事件
  • touchstart
  • touchmove
  • touchend
event.touches[0]
clientX
clientY
pageX
pageY
target

内存和性能

  1. 事件委托
  2. 及时移除事件处理程序

模拟事件

  1. 创建鼠标事件
var btn = document.getElementById('myBtn');
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false , false, 0, null);
btn.dispatchEvent(event)
  1. 创建自定义事件 DOM3级规范
var div = document.getElementById('myDiv'),
    event;
div.addEventListener('myEvent', function(event){
    alert('div' + event.detail)
})
document.addEventListener('myEvent', function(event){
    alert('document' + event.detail)
})
if(document.implementation.hasFeature('CustomEvents', '3.0')) {
    event = document.createEvent('CustomEvent');
    event.initCustromEvent('myEvent', true, false, 'hello world');
    div.dispatchEvent(event)
}
let myEvent = new Event('custome')
$el.addEventListener('custome', () => {
  console.log('custome')
})
$el.dispatchEvent(myEvent)

降低频率

具体事件

DOMContentLoader 与 window.onLoad

  • DOMContentLoaded 是 HTML 文档被加载和解析完成后触发
  • Load 是页面所有资源(包括图片,音频等)加载完后触发

表单

  1. 元素可以添加form属性
  2. 新增Input元素种类
  • search
  • url
  • email
  • range
  • color
  1. 表单事件
  • onchange

表单元素

  • label 表单控件标题
  • filed 表单控件集合
  • datalist 其他表单控件的数据列表

表单属性

  maxlength
  pattern
  list
  multiple
  required
  formnovalidate
  autofocus
  accept
  autocomplete

  enctype
  target
  name



表单验证

  1. required

存储

Cookie 4k

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

  • 大小:4kB 4096 个字节
  • name
  • value
  • domain
    • 如果不指定,默认为 当前文档的主机(Document.location)
    • 同一个域名下的二级域名也是不可以交换使用 cookie 的,比如 www.baidu.com 和 image.baidu.com
    • 设置 Domain=mozilla.org,二级域名 developer.mozilla.org 也会包含 cookie
  • path
    • 默认是 /
    • 子路径会被匹配
      • 如果设置了 Path=/docs,那么 /docs /docs/web /docs/web/http 都会被匹配
  • secure
    • 设置为 true 的话,只通过 HTTPS 协议发送给服务端
  • http-only
    • 为了避免 XSS 攻击,通过JavaScript的 Document.cookie API 无法访问带有 HttpOnly 标记的Cookie
    • 如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记
  • Expires/Max-Age
    • 不设置的话,是会话期 Cookie,浏览器关闭会自动被删除
    • 设置一个过期时间
  • SameSite
    • cookie 在跨站请求时不会被发送,从而可以阻止 CSRF
    • 这是一个较新的字段,所有主流浏览器已经得到支持
    • 以前,如果SameSite属性没有设置,或者没有得到运行浏览器的支持,那么它的行为等同于None,Cookies会被包含在任何请求中——包括跨站请求。
    • None
      • 不管同站、跨站都发送
    • Strict
      • 只在访问相同站点才会被发送
    • Lax
      • 宽松
      • 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外
        • 图片加载或者frames的调用,不发送 Cookie
        • ajax 不发送
        • a 链接跳转,发送 Cookie
      • 新版本浏览器为默认选项
        • 意味着如果没有设置 SameSite 属性时,将会视作为设置为 Lax
    • 一般来说,我们设置 secure=true 和 samesite = none & http-only = true就行了
  • 作用域
    • Domain 和 Path 标识了 Cookie 的作用域,即 Cookie 应该发给哪些 URL。
  • 过程
    • 第一次请求,服务端种下 cookie
    • 第二次请求,浏览器会自动带上 cookie,服务端会辨别身份

LocalStorage 5M

  • 大小
    • 5 MB

SessionStorage

  • 会话存储
    • 浏览器打开期间一直保持
      • 如果重新加载或者恢复页面仍会保持会话
    • 关闭浏览器即清除

IndexedDB

NoSQL 数据库,用键值对进行储存

动画

CSS 动画

JavaScript 动画

网络请求

XMLHttpRequest

Fetch

var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers: new Headers({
    'Content-Type': 'application/json'
  })
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response))

fetch-abort

  • 关于 AbortController,是window的一个API,用在fetch的第二个参数上,
  • 不支持 IE10,可以去 npm 搜 polyfill
  • 可以调用实例的 abort 方法,中断 fetch 请求
  • 可以监听 abort 事件
controller.signal.addEventListener('abort', rejectFn)

WebSocket

表单

表单字段

//选择表单元素
var selectbox = document.forms[0].elements['selectbox'];

//选择form
var form = document.getElementById('myForm')

重置表单

提交表单

<input type="submit">
<button type=“submit">
<input type="image">

// js方式提交表单
var form = document.getElementById('myForm')
form.submit()

重置表单

<input type="reset" value="rest btn">
<button type="reset">reset</button>

//阻止重置表单
var form = document.getElementById('myForm')
form.addEventListener('reset', function(e){
  e.preventDefault()
})

  1. input
  2. select
  3. 序列化
  4. 验证api
  required
  autofocus
  pattern
  checkValidiry()

新 API

MutationObserver 监听 DOM 变化

// 选择需要观察变动的节点
const targetNode = document.getElementById('some-id');

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
    // Use traditional 'for loops' for IE 11
    for(let mutation of mutationsList) {
        if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type === 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

IntersectionObserver 交叉观察者

// 1 图片懒加载
let observer = new IntersectionObserver(
(entries, observer) => { 
entries.forEach(entry => {
    /* 替换属性 */
    entry.target.src = entry.target.dataset.src;
    observer.unobserve(entry.target);
  });
}, 
{rootMargin: "0px 0px -200px 0px"});

document.querySelectorAll('img').forEach(img => { observer.observe(img) });


// 2 兴趣埋点
const boxList = [...document.querySelectorAll('.box')]

var io = new IntersectionObserver((entries) =>{
  entries.forEach(item => {
    // intersectionRatio === 1说明该元素完全暴露出来,符合业务需求
    if (item.intersectionRatio === 1) {
      // 。。。 埋点曝光代码
      io.unobserve(item.target)
    }
  })
}, {
  root: null,
  threshold: 1, // 阀值设为1,当只有比例达到1时才触发回调函数
})

// observe遍历监听所有box节点
boxList.forEach(box => io.observe(box))

PerformanceObserver 性能观察者

浏览器内部对Performance实现的观察者模式

function filterTime(a, b) {
  return (a > 0 && b > 0 && (a - b) >= 0) ? (a - b) : undefined;
}

let resolvePerformanceTiming = (timing) => {
  let o = {
    initiatorType: timing.initiatorType,
    name: timing.name,
    duration: parseInt(timing.duration),
    redirect: filterTime(timing.redirectEnd, timing.redirectStart), // 重定向
    dns: filterTime(timing.domainLookupEnd, timing.domainLookupStart), // DNS解析
    connect: filterTime(timing.connectEnd, timing.connectStart), // TCP建连
    network: filterTime(timing.connectEnd, timing.startTime), // 网络总耗时

    send: filterTime(timing.responseStart, timing.requestStart), // 发送开始到接受第一个返回
    receive: filterTime(timing.responseEnd, timing.responseStart), // 接收总时间
    request: filterTime(timing.responseEnd, timing.requestStart), // 总时间

    ttfb: filterTime(timing.responseStart, timing.requestStart), // 首字节时间
  };

  return o;
};

let resolveEntries = (entries) => entries.map(item => resolvePerformanceTiming(item));

let resources = {
  init: (cb) => {
    let performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance;
    if (!performance || !performance.getEntries) {
      return void 0;
    }

    if (window.PerformanceObserver) {
      let observer = new window.PerformanceObserver((list) => {
        try {
          let entries = list.getEntries();
          cb(resolveEntries(entries));
        } catch (e) {
          console.error(e);
        }
      });
      observer.observe({
        entryTypes: ['resource']
      })
    } else {
        window.addEventListener('load', () => {
        let entries = performance.getEntriesByType('resource');
        cb(resolveEntries(entries));
      });
    }
  },
};

requestAnimationFrame()

Binary data

ArrayBuffer 二进制数据

  • arrayBuffer
    • 二进制数组
    • 操作 ArrayBuffer,我们需要使用一个 view 视图
      • view 不存储数据,数据存在 ArrayBuffer 中
      • TypedArray 类型化数组
        • Uint8Array 8位无符号整数
      • DataView
        • 超灵活的“非类型化”视图
    • 场景
      • 主要在处理文件(创建、上传、下载)时遇到二进制数据
      • 图像处理遇到二进制
    • 2base64?
  1. typedArray
// 这将分配一个16字节的连续内存区域,并预先将其填充为零
let buffer = new ArrayBuffer(16); // create a buffer of length 16
let view = new Uint32Array(buffer);
alert(buffer.byteLength); // 16

alert(view.length); // 4, it stores that many integers
alert(view.byteLength); // 16, the size in bytes

// let's write a value
view[0] = 123456;

// iterate over values
for(let num of view) {
  alert(num); // 123456, then 0, 0, 0 (4 values total)
}
  1. DataView
let buffer = new Uint8Array([255, 255, 255, 255]).buffer;

let dataView = new DataView(buffer);

// get 8-bit number at offset 0
alert( dataView.getUint8(0) ); // 255

// now get 16-bit number at offset 0, that's 2 bytes, both with max value
alert( dataView.getUint16(0) ); // 65535 (biggest 16-bit unsigned int)

// get 32-bit number at offset 0
alert( dataView.getUint32(0) ); // 4294967295 (biggest 32-bit unsigned int)

dataView.setUint32(0, 0); // set 4-byte number to zero

Blob 二进制大对象

  • Blob 表示 具有类型的二进制数据
    • Blob 对象表示一个不可变、原始数据的类文件对象
  • 和 ArrayBuffer 有什么区别?
    • 是比 ArrayBuffer 更高级别的对象
  • 和 File 有什么区别?
    • File 对象内部使用 Blob
    • 从 <input> 标签中获取的File对象即是一个Blob实例
  • 不可修改
    • Blobs are immutable
    • 不能直接修改
    • 切割返回新的
  • 操作 Blob
    • 其实并不太会需要一个 arrayBuffer,不常用
    • 为什么要把 Blob 弄成 ArrayBuffer ?
      • 判断一个文件类型
        • 这样我们就可以对文件的字节进行读写了,比如说要判断一个文件的类型,就可以读取它的前两个字节,与 Hash 表进行匹配
  • blob url
    • 创建一个指向 blob 的 url,是一个映射,通过这个 url ,而可以访问 blob
    • img 和 a标签 都可以使用 blob url
    • 利用 URL.createObjectURL(blob)和 URL.revokeObjectURL()
    • link.href = URL.createObjectURL(blob)

  • 2base64
    • 不用 blob url 的另一个做饭是转换成 base64
    • 因为 img 同样支持 base64
    • 利用 FileReader
  • 应用
    • 判断文件类型
    • 图片翻转
      • 读取照片 EXIF 信息中的 Orientation 字段,以主动旋转照片
    • 下载图片
    • 通过 canvas 画图片,然后将 canvas 转换为 blob,然后将 blob 转化成 blob url,然后下载
// create Blob from a typed array and strings
let hello = new Uint8Array([72, 101, 108, 108, 111]); // "hello" in binary form

let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});

blob 转化成 base64

let link = document.createElement('a');
link.download = 'hello.txt';

let blob = new Blob(['Hello, world!'], {type: 'text/plain'});

let reader = new FileReader();
reader.readAsDataURL(blob); // converts the blob to base64 and calls onload

reader.onload = function() {
  link.href = reader.result; // data url
  link.click();
};

File 文件

  • File 对象继承自 Blob
  • 通常我们从 input 文件中获得 file 对象
  • 多数情况下,我们不必读取文件内容
    • 我们可以使用 URL.createObjectURL (file)创建一个简短的 url,并将其分配给一个或 img
  • FileReader 适用于任何 Blob,而不仅仅是 File
    • 使用它将一个 blob 转换为另一种格式
      • readAsArrayBuffer
      • readAsText
      • readAsDataURL 转化成 base64 data url

总结 - 操作二进制数据

  • File API
  • XMLHttpRequest
  • Fetch API
  • Canvas
  • Websocket

BOM

Browser Object Model,指的是当前窗口对象

document

就是我们的 DOM 对象啦

window 对象

setTimeouxxt setInterval

全局对象 下面的对象都是挂载在window对象下

  • 窗口位置
  • 窗口大小
  • 框架
  • 窗口关系
  • 导航和打开窗口
  • 系统对话框

window.open()打开一个弹窗

iframe

  • iframe.contentWindow
  • iframe.contentDocument
  • windows.top 最顶级父窗口的引用
  • window.parent 对外部窗口的引用
  • window.frames[] 该 iframe 还嵌套其他 iframe

跨窗口通信

  • 同源
    • do anthing
  • 不同源
    • 只能发消息 postMessage
    • 修改 location,无法读取 location
    • 可以访问 iframe.contentWindow
    • 不可以访问 iframe.contentDocument
  • 一个特例:相同二级域名的页面
    • 都设置 document.domain='site.com',可以使它们处于“同源”状态 点击劫持攻击

location 对象

window.location == document.location

  • hash
  • href
  • pathname
  • search

navigator 对象

  • 检测 UA

screen 对象

history 对象

保存着用户上网的历史记录,是window对象的属性。

history.go(-1)
history.go(2)

实践

Polyfills

  • IE 不支持 Promise

HTML5 Cross Browser Polyfills

First-Contentful-Paint 首屏时间

execCommand 复制文本

iframe 通信

  • 父子跨域通信
    • 父向子发送
      • iframe.contentWindow.postMessage()
      • window.addEventListener("message", receiveMessageFromIndex, false);
    • 子向父发送
      • parent.postMessage( {msg: 'MessageFromIframePage'}, '*');
      • window.addEventListener("message", receiveMessageFromIframePage, false);
  • 跨窗口通信
  • 跨标签通信

try...catch...错误处理

ES6 Module 和 CommonJS

pushState、hashchange 前端路由

Web Workers 单独运行线程

窗口的滚动

读取滚动值

window.pageYOffset/pageXOffset

设置滚动值

window.scrollTo(pageX,pageY) — 绝对定位
window.scrollBy(x,y) — 相对当前位置的滚动
elem.scrollIntoView(top) — 滚动到正好elem可视的位置(elem 与窗口的顶部/底部对齐) // top = true 顶部对齐

scrollIntoView

  • scrollIntoViewOptions
    • block 垂直对齐方式
      • start, center, end, or nearest. Defaults to start.
    • behavior
      • transition animation
    • inline
      • horizontal alignment
      • start, center, end, or nearest. Defaults to nearest
  • 此API基本所有浏览器都是支持的

scrollintoviewifneeded(true)

  • 没有有进入标准,很可能还会改,不推荐生产环境使用
  • 此API IE和 foxfire 不支持,所以 PC端 不能使用,但是移动端是没有问题
  • 只有一个值 true 是默认值,让元素在可视区域中居中对齐,false 可能顶部或者底部对齐,视元素靠哪边更近
var element = document.getElementById("box");

element.scrollIntoView(); // 默认值 true 和顶部对齐
element.scrollIntoView(false); // 和视口的底部对齐
element.scrollIntoView({block: "end"});
element.scrollIntoView({behavior: "smooth", block: "end", inline: 
"nearest"});

第三方 Cookie

如果 Cookie 的域和页面的域不同,称之为第三方 Cookie。

DOM 常见操作

document.createElement()
document.appendChild()
document.createTextNode()
document.getElementById()

removeChild(child);
getPropertyValue()

document.head || document.getElementByTagName('head')[0]
document.body
document.readyState == 'complete'

动态脚本

function loadScript(url){
  var script = document.createElement('script')
  script.type = 'text/javascript'
  script.src = url
  document.body.appendChild(script)
}

offset、scrollWidth

property 和 attribute

tagName 和 nodeName

DOM 序列化与反序列化

如何知道某个元素在页面上的偏移量?

function getElementLeft(el){
  var actualLeft = el.offsetLeft,
      current = el.offetParent;
  while(current !== null) {
    actualLeft += current.offsetParent;
    current = current.offsetParent;
  }
  return actualLeft;
}

多页面通信

  • 同源
    • 利用 Service Worker 中转
    • LocalStorage
      • LocalStorage 变化时,会触发 storage 事件
    • window.open + window.opener
      • 还是利用的 message 事件以及 postMessage 方法
  • 不同源

video 自动播放

日期格式化

实现 Canvas 缩放的方法

Canvas Retina 适配

rem 适配移动端屏幕

元素拖拽

横屏竖屏切换监听

前端动态生成屏幕快照

移动端虚拟键盘弹出输入框上移

判断元素是否在视窗内

H5软键盘兼容方案

注入 JS

元素是否可滚动

限制键盘输入

计算元素位置

访问 DOM 树的所有元素

获取 document 宽高

计算图片宽高

键盘密码可见切换

滚动到某个元素

ele.scrollIntoView({ behavior: 'smooth' });

addEventListener passive 被动

document.addEventListener('touchstart', onTouchStart, {passive: true});

查看是否支持 passive

var supportsPassive = false;
try {
  var opts = Object.defineProperty({}, 'passive', {
    get: function() {
      supportsPassive = true;
    }
  });
  window.addEventListener("testPassive", null, opts);
  window.removeEventListener("testPassive", null, opts);
} catch (e) {}

canvas 高倍屏 retina 下变模糊的处理办法

  • canvas 的 width、height属性用于管理画布尺寸【画布大小】
  • canvas 的 style 属性中的 width、height 正好是显示尺寸【实际渲染大小】
  • devicePixelRatio
    • 表示了屏幕的设备像素比,即用几个像素点宽度来渲染1个像素
  • backingStoreRatio
    • 决定了浏览器在渲染 canvas 之前会用几个像素来来存储画布信息
  • 根据当前屏幕的设备像素比 DPR
    • 然后将 canvas.width/hegith 放大到该设备像素比来绘制
    • 然后将 canvas.style.width/heigth 压缩到一倍来展示
var devicePixelRatio = window.devicePixelRatio || 1,
    backingStoreRatio = context.webkitBackingStorePixelRatio ||
                        context.mozBackingStorePixelRatio ||
                        context.msBackingStorePixelRatio ||
                        context.oBackingStorePixelRatio ||
                        context.backingStorePixelRatio || 1,

    ratio = devicePixelRatio / backingStoreRatio;

// upscale the canvas if the two ratios don't match
if(devicePixelRatio !== backingStoreRatio){

   // adjust the original width and height of the canvas
   // 调整画布的原始宽度和高度
   canvas.width = originalWidth * ratio;
   canvas.height = originalHeight * ratio;

   // scale the context to reflect the changes above
   // 缩放上下文以反映上述变化
   context.scale(ratio, ratio);
}

// ...do the drawing here...

// use CSS to bring the entire thing back to the original size
// 使用CSS将整个内容恢复到原始大小
canvas.style.width = originalWidth + 'px';
canvas.style.height = originalHeight + 'px';

数字键盘

<!-- 有"#" "*"符号输入 -->
<input type="tel">

<!-- 纯数字 -->
<input pattern="\d*">

系统功能

<!-- 拨号 -->
<a href="tel:10086">打电话给: 10086</a>

<!-- 发送短信 -->
<a href="sms:10086">发短信给: 10086</a>

<!-- 发送邮件 -->
<a href="mailto:839626987@qq.com">发邮件给:839626987@qq.com</a>

<!-- 选择照片或者拍摄照片 -->
<input type="file" accept="image/*">

<!-- 选择视频或者拍摄视频 -->
<input type="file" accept="video/*">

<!-- 多选 -->
<input type="file" multiple>

打开 APP

<a href="weixin://">打开微信</a>
<a href="alipays://">打开支付宝</a>
<a href="alipays://platformapi/startapp?saId=10000007">打开支付宝的扫一扫功能</a>
<a href="alipays://platformapi/startapp?appId=60000002">打开支付宝的蚂蚁森林</a>

忽略自动识别

<!-- 忽略浏览器自动识别数字为电话号码 -->
<meta name="format-detection" content="telephone=no">

<!-- 忽略浏览器自动识别邮箱账号 -->
<meta name="format-detection" content="email=no">


解决 input 失焦后页面没有回弹

一般出现在 iOS 设备中的微信内部浏览器,

出现的条件为:

  1. 页面高度过小
  2. 聚焦时,页面需要往上移动的时候

所以一般input在页面上方或者顶部都不会出现无法回弹🤣

解决办法为

  • 在聚焦时,获取当前滚动条高度
  • 然后失焦时,赋值之前获取的高度:
<template>
  <input type="text" @focus="focus" @blur="blur">
</template>

<script>
  export default {
    data() {
      return {
        scrollTop: 0
      }
    },
    
    methods: {
      focus() {
        this.scrollTop = document.scrollingElement.scrollTop;
      },
      
      blur() {
        document.scrollingElement.scrollTo(0, this.scrollTop);
      }
    }
  }
</script>

解决 iOS 中 input 失焦后,页面上移,点击事件无效

var u = navigator.userAgent;
var flag;
var timer;
var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isIOS) {
    document.body.addEventListener('focusin', () => { //软键盘弹起事件
        flag = true;
        clearTimeout(timer);
    })
    document.body.addEventListener('focusout', () => { //软键盘关闭事件
        flag = false;
        if (!flag) {
            timer = setTimeout(() => {
                window.scrollTo({
                    top: 0,
                    left: 0,
                    behavior: "smooth"
                }) //重点  =======当键盘收起的时候让页面回到原始位置(这里的top可以根据你们个人的需求改变,并不一定要回到页面顶部)
            }, 200);
        } else {
            return false;
        }
    })
} else {
    return false;
}

intersection Observer

  • 了解某个元素是否进入了"视口"(viewport),即用户能不能看到它
    • 以前,监听 scroll 事件,判断对应视口左上角的坐标
      • 容易造成性能问题
    • 现在,有了 intersection Observer 交叉观察器
  • 可见(visible)的本质是,目标元素与视口产生一个交叉区
    • 当页面滚动时,懒加载图片或其他内容
    • 为计算广告收益,检测其广告元素的曝光情况
  • intersectionRatio
    • 返回目标元素出现在可视区的比例

var intersectionObserver = new IntersectionObserver(function(entries) {

  // If intersectionRatio is 0, the target is out of view
  // and we do not need to do anything.

  if (entries[0].intersectionRatio <= 0) return;

  loadItems(10);
  console.log('Loaded new items');
});

// start observing
// 开始监听一个目标元素
intersectionObserver.observe(document.querySelector('.scrollerFooter'));
Last Updated: 7/21/20, 12:45 PM
Contributors: wangqi