1 基本概念

1.1 DOM树

W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
- 文档:一个页面就是一个文档,DOM中使用document表示
- 节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
- 标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示
1.2 获取元素
// 根据ID获取document.getElementById('time');// 根据标签名获取元素,返回元素对象动态集合,伪数组document.getElementsByTagName('标签名')// 根据类名返回元素document.getElementsByClassName('类名');// 根据指定选择器返回第一个元素对象document.querySelector('选择器');// 根据指定选择器返回document.querySelectorAll('选择器');
1.3 获取特殊元素(body, html)
// 返回body元素对象doucumnet.body// 获取html元素document.documentElement
2 事件
2.1 常见鼠标事件

3 操作元素和属性
3.1 改变元素内容
element.innerTextelement.innerHTML
innerText 它去除 html 标签, 同时空格和换行也会去掉
innerHTML 包括 html 标签,同时保留空格和换行
3.2 常用元素属性操作
// 获取属性值element.属性名// 获取属性值element.属性名 = 值
例如:
img.src = 'images/zxy.jpg';
修改img标签的src的值
表单元素的属性:
type、value、checked、selected、disabled
3.3 样式属性操作
// 行内样式操作element.style// 如box.style.display = 'none'this.style.backgroundColor = 'purple';// 类名样式操作element.classNamethis.className = 'first change';
style要采取驼峰命名法,如fontSize、 backgroundColor
className 会直接更改元素的类名,会覆盖原先的类名。
3.4 排他思想
var btns = document.querySelectorAll('button')for (let i = 0; i < btns.length; i++) {btns[i].onclick = function() {for (let j = 0; j < btns.length; j++) {btns[j].className = ''}this.className = 'btn'}}
3.5 自定义属性
- element.属性 获取属性值。获取内置属性值(元素本身自带的属性)
- element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性
- element.setAttribute(‘属性’); 主要设置自定义的属性 (标准)
- element.removeAttribute(‘属性’); 移除属性
H5规定自定义属性data-开头做为属性名并且赋值。
H5新增 element.dataset.index 或者 element.dataset[‘index’]
3.6 获取元素css当前属性
let style = document.defaultView.getComputedStyle(elem, null);// 获取bottom的值style.bottom
elem: 用于获取计算样式的[Element](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)。
4 节点操作
本质:利用父子兄节点关系获取元素
网页中的所有内容都是节点(标签、属性、文本、注释等)
节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点 nodeType 为1
- 属性节点 nodeType 为2
- 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
4.1 父级节点
node.parentNode
- parentNode 属性可返回某节点的父节点,注意是最近的一个父节点
- 如果指定的节点没有父节点则返回 null
4.2 子节点
返回包含指定节点的子节点的集合,该集合为即时更新的集合parentNode.childNodes
返回值里面包含了所有的子节点,包括元素节点,文本节点等。不提倡使用childNodes,因为要做特殊处理var ul = document.querySelector('ul');for (var i = 0; i < ul.childNodes.length; i++) {if (ul.childNodes[i].nodeType == 1) {// ul.childNodes[i] 是元素节点console.log(ul.childNodes[i]);}}
parentNode.children
是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 (这个是我们重点掌握的)。
其他子节点获取
parentNode.firstChildparentNode.lastChild
找不到则返回null。会返回文本节点(空格之类)
其他子节点获取
parentNode.firstElementChildparentNode.lastElementChild
返回第一个子元素节点和最后子元素节点,找不到则返回null。
其他方法
parentNode.chilren[0]parentNode.chilren[parentNode.chilren.length- 1]
4.3 兄弟节点
node.nextSiblingnode.previousSibling
返回当前元素的下一个兄弟元素节点和上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。
node.nextElementSiblingnode.previousElementSibling
返回当前元素下一个兄弟元素节点和前一个,找不到则返回null
4.4 创建节点
动态创建元素节点。
document.createElement('tagName')
4.5 添加节点

node.appendChild(child)
将一个节点添加到指定父节点的子节点列表末尾
node.insertBefore(child, 指定元素) // 参数1:创建的元素, 参数2:要插入指定的子节点
将一个节点添加到父节点的指定子节点前面
示例:
<ul><li>i</li><li>i</li><li>i</li><li>i</li></ul><script>var newLi = document.createElement('li')newLi.innerHTML = 'one'var ul = document.querySelector('ul');ul.insertBefore(newLi, ul.children[0])</script>

element.insertAdjacentHTML(position, text);// 例:var li = '<li class="liactive"><span>新选项卡</span></li>';// 把这两个元素追加到对应的父元素里面ul.insertAdjacentHTML('beforeend', li);
'beforebegin':元素自身的前面。'afterbegin':插入元素内部的第一个子节点之前。'beforeend':插入元素内部的最后一个子节点之后。'afterend':元素自身的后面。

4.6 删除节点
node.removeChild(child)ul.removeChild(ul.children[3])
4.7 复制节点
node.cloneNode()
方法返回调用该方法的节点的一个副本
- 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
- 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
4.8 替换节点
parentNode.replaceChild(newChild, oldChild);
4.9 三种创建元素区别
- document.write()
- element.innerHTML
document.createElement()
<br />1. document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘<br />2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘<br />3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂<br />4. createElement() 创建多个元素效率稍低一点点,但是结构更清晰<br />
var arr = [];for (var i = 0; i <= 100; i++) {arr.push('<a href="#">百度</a>');}inner.innerHTML = arr.join('');
最快
5 事件
5.1 方法监听注册
eventTarget.addEventListener(type, listener[, useCapture])
方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。
- type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
- listener:事件处理函数,事件发生时,会调用该监听函数
useCapture:可选参数,是一个布尔值,默认是 false。参数是 true 那么在捕获阶段触发处理程序
<br />IE8 及早期版本支持
eventTarget.attachEvent(eventNameWithOn, callback)
eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
callback: 事件处理函数,当目标触发事件时回调函数被调用
注册事件兼容性function addEventListener(element, eventName, fn) {// 判断当前浏览器是否支持 addEventListener 方法if (element.addEventListener) {element.addEventListener(eventName, fn); // 第三个参数 默认是false} else if (element.attachEvent) {element.attachEvent('on' + eventName, fn);} else {// 相当于 element.onclick = fn;element['on' + eventName] = fn;}}
5.2 删除事件
```javascript // 传统方式 eventTarget.onclick = null;
// 方法监听注册 eventTarget.removeEventListener(type, listener[, useCapture]); eventTarget.detachEvent(eventNameWithOn, callback);
兼容```javascriptfunction removeEventListener(element, eventName, fn) {// 判断当前浏览器是否支持 removeEventListener 方法if (element.removeEventListener) {element.removeEventListener(eventName, fn); // 第三个参数 默认是false} else if (element.detachEvent) {element.detachEvent('on' + eventName, fn);} else {element['on' + eventName] = null;}}
5.3 DOM事件流
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。
DOM 事件流分为3个阶段:
1. 捕获阶段
2. 当前目标阶段
3. 冒泡阶段
事件冒泡: IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
事件捕获: 网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。
通过事件冒泡传递信息, 也就是事件委托

些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
5.4 事件对象
event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。它有很多属性和方法。
eventTarget.onclick = function (event) { }eventTarget.addEventListener('click', function (event) { })
当我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
兼容性
var div = document.querySelector('div');div.onclick = function(e) {// 事件对象e = e || window.event;console.log(e);}
在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找
5.5 阻止默认行为
html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。form表单的默认提交
a.onclick = function(e) {// 普通浏览器 e.preventDefault(); 方法e.preventDefault();// 低版本浏览器 ie678 returnValue 属性e.returnValue = false;// 我们可以利用return false 也能阻止默认行为 没有兼容性问题return false;}
5.6 阻止冒泡
son.addEventListener('click', function(e) {if (e && e.stopPropagation) {e.stopPropagation(); // stop 停止 Propagation 传播} else {window.event.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡}});
非标准写法:IE 6-8 利用事件对象 cancelBubble 属性
5.7 事件委托
给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
只操作了一次 DOM ,提高了程序的性能。
var ul = document.querySelector('ul')ul.addEventListener('click', function(e) {console.log(e.target);})
5.8 常见鼠标事件
mouseenter 和mouseover的区别
- 当鼠标移动到元素上时就会触发mouseenter 事件
- 类似 mouseover,它们两者之间的差别是
- mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发
- 之所以这样,就是因为mouseenter不会冒泡
- 跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡
5.9 禁止鼠标右键菜单
contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单
document.addEventListener('contextmenu', function (e) {e.preventDefault();})
相当于右键失效
5.10 禁止鼠标选中(selectstart 开始选中)
document.addEventListener('selectstart', function(e) {e.preventDefault();})
5.11 鼠标事件对象
5.12 键盘事件

三个事件的执行顺序是: keydown — keypress —- keyup
5.13 键盘事件对象

- onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
- 在我们实际开发中,我们更多的使用keydown和keyup, 它能识别所有的键(包括功能键)
- Keypress 不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值
// 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值document.addEventListener('keyup', function(e) {console.log('up:' + e.keyCode);// 我们可以利用keycode返回的ASCII码值来判断用户按下了那个键if (e.keyCode === 65) {alert('您按下的a键');} else {alert('您没有按下a键')}})
5.14 点击和删除函数
var btn = document.querySelector('button')setTimeout(function(){btn.click()},3000)btn.onclick = function(){btn.remove()}
5.15 双击禁止选中文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
5.16 失去焦点函数
input.onkeyup = function(e) {if (e.keyCode === 13) {// 手动调用表单失去焦点事件 不需要鼠标离开操作this.blur();}
5.17 表单处于选中
var ipt = document.querySelector('input')ipt.select()
6 BOM
BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
BOM 比 DOM 更大,它包含 DOM。
6.1 顶级对象window
window 对象是浏览器的顶级对象,它具有双重角色
- 它是 JS 访问浏览器窗口的一个接口。
- 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。
注意:window下的一个特殊属性 window.name
6.2 页面(窗口)加载事件
window.onload = function(){}// 或者window.addEventListener("load",function(){});
window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等), 就调用的处理函数。
- window.onload 传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload 为准。
- 如果使用 addEventListener 则没有限制
6.3 窗口加载事件
document.addEventListener('DOMContentLoaded',function(){})
DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。
6.4 调整窗口大小事件
window.onresize = function(){}window.addEventListener("resize",function(){});
window.onresize 是调整窗口大小加载事件, 当触发时就调用的处理函数。
注意:
- 只要窗口大小发生像素变化,就会触发这个事件。
- 我们经常利用这个事件完成响应式布局。 window.innerWidth 当前屏幕的宽度
// 注册页面加载事件window.addEventListener('load', function() {var div = document.querySelector('div');// 注册调整窗口大小事件window.addEventListener('resize', function() {// window.innerWidth 获取窗口大小console.log('变化了');if (window.innerWidth <= 800) {div.style.display = 'none';} else {div.style.display = 'block';}})})
6.5 定时器
window 对象给我们提供了 2 个非常好用的方法-定时器。
- setTimeout()
- setInterval()
6.6 炸弹定时器setTimeout
window.setTimeout(调用函数, [延迟的毫秒数]);
调用函数可以直接写函数,或者写函数名或者采取字符串‘函数名()’三种形式。第三种不推荐
延迟的毫秒数省略默认是 0,如果写,必须是毫秒
6.7 停止定时器clearTimeout
window.clearTimeout(timeoutID)
示例:
var btn = document.querySelector('button');var timer = setTimeout(function() {console.log('爆炸了');}, 5000);btn.addEventListener('click', function() {// 停止定时器clearTimeout(timer);})
6.8 闹钟定时器setInterval
window.setInterval(回调函数, [间隔的毫秒数]);
6.9 停止定时器clearInterval
window.clearInterval(intervalID);
7 this指向问题
- 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
- 方法调用中谁调用this指向谁
- 构造函数中this指向构造函数的实例
// 1. 全局作用域或者普通函数中this指向全局对象window( 注意定时器里面的this指向window)console.log(this);function fn() {console.log(this);}window.fn();window.setTimeout(function() {console.log(this);}, 1000);// 2. 方法调用中谁调用this指向谁var o = {sayHi: function() {console.log(this); // this指向的是 o 这个对象}}o.sayHi();var btn = document.querySelector('button');btn.addEventListener('click', function() {console.log(this); // 事件处理函数中的this指向的是btn这个按钮对象})// 3. 构造函数中this指向构造函数的实例function Fun() {console.log(this); // this 指向的是fun 实例对象}var fun = new Fun();
8 JS 执行机制
JavaScript 语言的一大特点就是单线程, 因为对某个 DOM 元素进行添加和删除操作,不能同时进行。
8.1 同步和异步
同步:前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。
异步:做这件事的同时,你还可以去处理其他事情
JS中所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
- 同步任务指的是:
- 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 异步任务指的是:
- 不进入主线程、而进入”任务队列”的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。
一般而言,异步任务有以下三种类型:
1、普通事件,如 click、resize 等
2、资源加载,如 load、error 等
3、定时器,包括 setInterval、setTimeout 等异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)。
8.2 JS执行机制
- 先执行执行栈中的同步任务。
- 异步任务(回调函数)放入任务队列中。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
8.3 事件循环


由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。
9 location 对象
window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为 这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。
9.1 URL
统一资源定位符 (Uniform Resource Locator, URL) 是互联网上标准资源的地址。互联网上的每个文件都有 一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
protocol://host[:port]/path/[?query]#fragmenthttp://www.itcast.cn/index.html?name=andy&age=18#link
9.2 location 对象的属性
9.3 location对象的常见方法

// 记录浏览历史,所以可以实现后退功能location.assign('http://www.baidu.com');// 不记录浏览历史,所以不可以实现后退功能location.replace('http://www.baidu.com');location.reload(true);
10 navigator 对象
navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客 户机发送服务器的 user-agent 头部的值。
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {window.location.href = ""; //手机} else {window.location.href = ""; //电脑}
11 history对象
window对象给我们提供了一个 history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。
history.go(-1) // 返回上一页
12 元素偏移量offset 系列
offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
- 获得元素距离带有定位父元素的位置
- 获得元素自身的大小(宽度高度)
- 注意:返回的数值都不带单位
12.1 offset与style区别
offset:
- offset 可以得到任意样式表中的样式值
- offset 系列获得的数值是没有单位的
- offsetWidth 包含padding+border+width
- offsetWidth 等属性是只读属性,只能获取不能赋值
所以,我们想要获取元素大小位置,用offset更合适
style:
- style 只能得到行内样式表中的样式值
- style.width 获得的是带有单位的字符串
- style.width 获得不包含padding和border 的值
- style.width 是可读写属性,可以获取也可以赋值
所以,我们想要给元素更改值,则需要用style改变
12.2 获取鼠标在盒子内的坐标
- 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
- 首先得到鼠标在页面中的坐标(e.pageX, e.pageY)
- 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
- 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标
如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动
var box = document.querySelector('.box');box.addEventListener('mousemove', function(e) {var x = e.pageX - this.offsetLeft;var y = e.pageY - this.offsetTop;this.innerHTML = 'x坐标是' + x + ' y坐标是' + y;})
12.3 移动盒子案例
// 按下鼠标title.addEventListener('mousedown', (e) => {// 得到鼠标在页面中的坐标(e.pageX, e.pageY)// 得到盒子在页面中的距离 ( login.offsetLeft, login.offsetTop)// 求出鼠标在盒子内的坐标var mou_x = e.pageX - login.offsetLeftvar mou_y = e.pageY - login.offsetTop// 鼠标移动document.addEventListener('mousemove', move)// 盒子移动function move(e) {// 鼠标在页面坐标 - 鼠标在盒子里的坐标 = 盒子的坐标login.style.left = e.pageX - mou_x + 'px'login.style.top = e.pageY - mou_y + 'px'}document.addEventListener('mouseup', () => {// 放开鼠标,清除事件document.removeEventListener('mousemove', move)})})
12.4 放大镜
```javascript // 大框 var preview_img = this.document.querySelector(‘.preview_img’) // 黄色遮挡层 var mask = this.document.querySelector(‘.mask’) // 放大镜部分 var big = this.document.querySelector(‘.big’) var bigImg = this.document.querySelector(‘.bigImg’)
// 鼠标移入显示隐藏 preview_img.addEventListener(‘mousemove’, (e) => { mask.style.display = ‘block’ big.style.display = ‘block’ // 鼠标在盒子内坐标 var mou_x = e.pageX - preview_img.offsetLeft var mou_y = e.pageY - preview_img.offsetTop // 让鼠标在黄色遮挡层居中 var x = mou_x - mask.offsetWidth / 2 var y = mou_y - mask.offsetWidth / 2
// 预防黄色遮挡层跟着鼠标移出框外,遮挡层的最大移动距离 if (x <= 0) { x = 0 } else if (x > preview_img.offsetWidth - mask.offsetWidth) { x = preview_img.offsetWidth - mask.offsetWidth }
if (y <= 0) { y = 0 } else if (y > preview_img.offsetWidth - mask.offsetWidth) { y = preview_img.offsetWidth - mask.offsetWidth } mask.style.left = x + ‘px’ mask.style.top = y + ‘px’
// 大图片移动距离,大图片的移动距离 = 遮挡层移动距离 大图片最大移动距离 / 遮挡层的最大移动距离 bigImg.style.left = (x (big.offsetWidth - bigImg.offsetWidth)) / (preview_img.offsetWidth - mask.offsetWidth) + ‘px’ bigImg.style.top = (y * (big.offsetWidth - bigImg.offsetWidth)) / (preview_img.offsetWidth - mask.offsetWidth) + ‘px’ })
<a name="BMtk3"></a># 13 元素可视区 client 系列client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。<br /><br /><a name="n2omp"></a># 14 立即执行函数立即执行函数 `(function() {})()` 或者 `(function(){}())`<br />主要作用: 创建一个独立的作用域。 避免了命名冲突问题可以看作匿名函数自调用<br /><a name="RbBBa"></a># 15 物理像素比与字体大小应用```javascript// 获取的html 的根元素var docEl = document.documentElement// dpr 物理像素比var dpr = window.devicePixelRatio || 1// adjust body font size 设置我们body 的字体大小function setBodyFontSize() {// 如果页面中有body 这个元素 就设置body的字体大小if (document.body) {document.body.style.fontSize = (12 * dpr) + 'px'} else {// 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body// 的字体大小document.addEventListener('DOMContentLoaded', setBodyFontSize)}}setBodyFontSize();// set 1rem = viewWidth / 10 设置我们html 元素的文字大小function setRemUnit() {var rem = docEl.clientWidth / 10docEl.style.fontSize = rem + 'px'}setRemUnit()// reset rem unit on page resize 当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小window.addEventListener('resize', setRemUnit)// pageshow 是我们重新加载页面触发的事件window.addEventListener('pageshow', function(e) {// e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小if (e.persisted) {setRemUnit()}})
在重新加载页面中,pageshow会在load事件触发后触发;
16 元素滚动scroll 系列属性
scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。
16.1 元素被卷去头部和页面被卷去头部区别
页面被卷去的头部:可以通过window.pageYOffset 获得
元素被卷去的头部是 element.scrollTop
16.2 滚动页面事件
滚动条在滚动时会触发 onscroll 事件。
固定侧边栏案例:
// 原始距离顶部大小var bannnerTop = banner.offsetTop// 侧边栏距离banner顶部的距离var sliderTop = slider.offsetTop - bannnerTopdocument.addEventListener('scroll', ()=>{if( window.pageYOffset >= bannnerTop){slider.style.position = 'fixed'slider.style.top = sliderTop + 'px'slider.children[0].style.display = 'block'} else {slider.style.position = 'absolute'slider.style.top = 300 + 'px'slider.children[0].style.display = 'none'}})
16.3 页面被卷去的头部兼容性解决方案
function getScroll() {return {left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0};}// 使用的时候getScroll().left
17 三大系列区别
1.offset系列 经常用于获得元素位置 offsetLeft offsetTop
2.client经常用于获取元素大小 clientWidth clientHeight
3.scroll 经常用于获取滚动距离 scrollTop scrollLeft
4.注意页面滚动的距离通过 window.pageXOffset 获得
18 动画封装
function animate(obj, target) {// 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器// 解决方案就是 让我们元素只有一个定时器执行// 先清除以前的定时器,只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {if (obj.offsetLeft >= target) {// 停止动画 本质是停止定时器clearInterval(obj.timer);}obj.style.left = obj.offsetLeft + 1 + 'px';}, 30);}
18.1 缓慢动画效果
- 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
- 核心算法: (目标值 - 现在的位置) / 10 做为每次移动的距离步长
- 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
- 注意步长值需要取整
如果是正值,则步长 往大了取整,如果是负值,则步长向小了取整
function animate(obj, target, callback) {// 先清除以前的定时器,只保留当前的一个定时器执行clearInterval(obj.timer);obj.timer = setInterval(function() {// 步长值写到定时器的里面// 把我们步长值改为整数 不要出现小数的问题var step = (target - obj.offsetLeft) / 10;// 如果是正值,则步长 往大了取整,如果是负值,则步长向小了取整step = step > 0 ? Math.ceil(step) : Math.floor(step);if (obj.offsetLeft == target) {// 停止动画 本质是停止定时器clearInterval(obj.timer);// 回调函数写到定时器结束里面callback && callback();}// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10obj.style.left = obj.offsetLeft + step + 'px';}, 15);}
Math.ceil() // 向上取整
Math.floor() // 向下取整
18.2 页面滚动
window.scroll(x,y)window.scroll(0, 0);
返回顶部案例:
// 点击返回顶部var goBack = document.querySelector('.goBack')goBack.addEventListener('click', function () {animation(window, 0)})function animation(obj, target, callback) {clearInterval(obj.timer)obj.timer = setInterval(() => {// 向上取整,不要有小数影响var step = (target - obj.pageYOffset) / 10step = step > 0 ? Math.ceil(step) : Math.floor(step);if (obj.pageYOffset == target) {clearInterval(obj.timer)callback && callback();}obj.scroll(0, obj.pageYOffset + step)}, 15);}
19 节流阀
防止按钮连续点击造成播放过快
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
如:
开始设置一个变量var flag= true;
if(flag){flag = false; do something} 关闭水龙头
利用回调函数动画执行完毕, flag = true 打开水龙头
// 截流阀,用于等一张图片切换完再打开var flag = true// 点击右键滚动arrow_r.addEventListener('click', function () {if(flag) {flag = false // 关闭水龙头if (num == ul.children.length - 1) {num = 0ul.style.left = 0}num++animate(ul, -num * focusWidth, ()=>{flag = true // 开启水龙头})// 小圆点跟随图片动circle_num++if (circle_num == circle.children.length) {circle_num = 0}circleChange()}})
20 触屏事件
- 移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。
- 比如触屏事件 touch(也称触摸事件),Android和 IOS 都有。
- touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。
- 触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
20.1 触摸事件对象(TouchEvent)
TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。
这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等
因为平时我们都是给元素注册触摸事件,所以重点记住 ``**targetTocuhes**
20.2 移动端拖动元素案例
- touchstart、touchmove、touchend可以实现拖动元素
- 拖动元素需要当前手指的坐标值 ,使用 targetTouches[0] 里面的pageX 和 pageY
盒子原来的位置 + 手指移动的距离
var div = document.querySelector('div')// 初始位置var start_x = 0var start_y = 0// 盒子原来的坐标var box_x = 0var box_y = 0div.addEventListener('touchstart', function (e) {// 当前手指坐标值start_x = e.targetTouches[0].pageXstart_y = e.targetTouches[0].pageY// 盒子位置box_x = this.offsetLeftbox_y = this.offsetTopconsole.log('touchstart');})div.addEventListener('touchmove', function (e) {// 计算手指的移动距离var move_x = e.targetTouches[0].pageX - start_xvar move_y = e.targetTouches[0].pageY - start_y// 盒子移动的距离this.style.left = box_x + move_x + 'px'this.style.top = box_y + move_y + 'px'// 阻止屏幕滚动的默认行为e.preventDefault()console.log('touchmove');})
仿pc端写法:
var div = document.querySelector('div')div.addEventListener('touchstart', function (e) {// 鼠标在盒子的位置var mou_x = e.targetTouches[0].pageX - this.offsetLeftvar mou_y = e.targetTouches[0].pageY - this.offsetTopdocument.addEventListener('touchmove', move)function move(e) {// 手指移动变化的位置 - 手指在盒子的位置 = 盒子变化的位置div.style.left = e.targetTouches[0].pageX - mou_x + 'px'div.style.top = e.targetTouches[0].pageY - mou_y + 'px'// 阻止屏幕滚动的默认行为e.preventDefault()}document.addEventListener('touchend', () => {// 放开鼠标,清除事件document.removeEventListener('touchmove', move)})})
21 classList 属性
HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。 ```javascript // 添加类 element.classList.add(’类名’); focus.classList.add(‘current’);
// 移除类 element.classList.remove(’类名’); focus.classList.remove(‘current’);
// 切换类 element.classList.toggle(’类名’); focus.classList.toggle(‘current’);
<a name="6ZxuH"></a># 22 移动端click延时移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。<br />解决方案:<br />1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟。```html<meta name="viewport" content="user-scalable=no">
使用插件。 fastclick 插件解决 300ms 延迟。
GitHub官网地址: https://github.com/ftlabs/fastclick
if ('addEventListener' in document) {document.addEventListener('DOMContentLoaded', function() {FastClick.attach(document.body);}, false);}
23 本地存储
- 数据存储在用户浏览器中
- 容量较大,sessionStorage约5M、localStorage约20M
- 只能存储字符串,可以将对象JSON.stringify() 编码后存储
23.1 sessionStorage
1、生命周期为关闭浏览器窗口
2、在同一个窗口(页面)下数据可以共享
3、以键值对的形式存储使用
// 存储数据:sessionStorage.setItem(key, value)// 获取数据sessionStorage.getItem(key)// 删除数据sessionStorage.removeItem(key)// 清空数据:(所有都清除掉)sessionStorage.clear()
23.2 localStorage
- 声明周期永久生效,除非手动删除 否则关闭页面也会存在
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用 ```javascript // 存储数据 localStorage.setItem(key, value)
// 获取数据 localStorage.getItem(key)
// 删除数据 localStorage.removeItem(key)
// 清空数据 localStorage.clear() ```


