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.innerText
element.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.className
this.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.firstChild
parentNode.lastChild
找不到则返回null。会返回文本节点(空格之类)
其他子节点获取
parentNode.firstElementChild
parentNode.lastElementChild
返回第一个子元素节点和最后子元素节点,找不到则返回null。
其他方法
parentNode.chilren[0]
parentNode.chilren[parentNode.chilren.length- 1]
4.3 兄弟节点
node.nextSibling
node.previousSibling
返回当前元素的下一个兄弟元素节点和上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。
node.nextElementSibling
node.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);
兼容
```javascript
function 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]#fragment
http://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.offsetLeft
var 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’ })
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1688721/1626571549520-a55fc0d1-155f-4a35-9989-00c7430299c3.png#crop=0&crop=0&crop=1&crop=1&height=130&id=XcRsE&margin=%5Bobject%20Object%5D&name=image.png&originHeight=260&originWidth=1776&originalType=binary&ratio=1&rotation=0&showTitle=false&size=69848&status=done&style=none&title=&width=888)
<a name="BMtk3"></a>
# 13 元素可视区 client 系列
client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1688721/1626572184678-c0915ee8-23ce-4b38-9c34-972711e8da50.png#crop=0&crop=0&crop=1&crop=1&height=203&id=bQwZq&margin=%5Bobject%20Object%5D&name=image.png&originHeight=406&originWidth=1464&originalType=binary&ratio=1&rotation=0&showTitle=false&size=254983&status=done&style=none&title=&width=732)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1688721/1626572224122-7c828118-a9fe-40c5-841c-a1891565ce43.png#crop=0&crop=0&crop=1&crop=1&height=221&id=ofGGt&margin=%5Bobject%20Object%5D&name=image.png&originHeight=442&originWidth=826&originalType=binary&ratio=1&rotation=0&showTitle=false&size=78128&status=done&style=none&title=&width=413)
<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 / 10
docEl.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 - bannnerTop
document.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 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
obj.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) / 10
step = 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 = 0
ul.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 = 0
var start_y = 0
// 盒子原来的坐标
var box_x = 0
var box_y = 0
div.addEventListener('touchstart', function (e) {
// 当前手指坐标值
start_x = e.targetTouches[0].pageX
start_y = e.targetTouches[0].pageY
// 盒子位置
box_x = this.offsetLeft
box_y = this.offsetTop
console.log('touchstart');
})
div.addEventListener('touchmove', function (e) {
// 计算手指的移动距离
var move_x = e.targetTouches[0].pageX - start_x
var 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.offsetLeft
var mou_y = e.targetTouches[0].pageY - this.offsetTop
document.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() ```