1 基本概念

Web APIs - 图1

1.1 DOM树

image.png
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。

  • 文档:一个页面就是一个文档,DOM中使用document表示
  • 节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
  • 标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示

1.2 获取元素

  1. // 根据ID获取
  2. document.getElementById('time');
  3. // 根据标签名获取元素,返回元素对象动态集合,伪数组
  4. document.getElementsByTagName('标签名')
  5. // 根据类名返回元素
  6. document.getElementsByClassName('类名');
  7. // 根据指定选择器返回第一个元素对象
  8. document.querySelector('选择器');
  9. // 根据指定选择器返回
  10. document.querySelectorAll('选择器');

1.3 获取特殊元素(body, html)

  1. // 返回body元素对象
  2. doucumnet.body
  3. // 获取html元素
  4. document.documentElement

2 事件

2.1 常见鼠标事件

image.png

3 操作元素和属性

image.png

3.1 改变元素内容

  1. element.innerText
  2. element.innerHTML

innerText 它去除 html 标签, 同时空格和换行也会去掉
innerHTML 包括 html 标签,同时保留空格和换行

3.2 常用元素属性操作

  1. // 获取属性值
  2. element.属性名
  3. // 获取属性值
  4. element.属性名 =

例如:

  1. img.src = 'images/zxy.jpg';

修改img标签的src的值

表单元素的属性:

  1. typevaluecheckedselecteddisabled

3.3 样式属性操作

  1. // 行内样式操作
  2. element.style
  3. // 如
  4. box.style.display = 'none'
  5. this.style.backgroundColor = 'purple';
  6. // 类名样式操作
  7. element.className
  8. this.className = 'first change';

style要采取驼峰命名法,如fontSize、 backgroundColor
className 会直接更改元素的类名,会覆盖原先的类名。

3.4 排他思想

  1. var btns = document.querySelectorAll('button')
  2. for (let i = 0; i < btns.length; i++) {
  3. btns[i].onclick = function() {
  4. for (let j = 0; j < btns.length; j++) {
  5. btns[j].className = ''
  6. }
  7. this.className = 'btn'
  8. }
  9. }

先清空

3.5 自定义属性

  • element.属性 获取属性值。获取内置属性值(元素本身自带的属性)
  • element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性
  • element.setAttribute(‘属性’); 主要设置自定义的属性 (标准)
  • element.removeAttribute(‘属性’); 移除属性

H5规定自定义属性data-开头做为属性名并且赋值。
H5新增 element.dataset.index 或者 element.dataset[‘index’]

3.6 获取元素css当前属性

  1. let style = document.defaultView.getComputedStyle(elem, null);
  2. // 获取bottom的值
  3. 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 父级节点

  1. node.parentNode
  • parentNode 属性可返回某节点的父节点,注意是最近的一个父节点
  • 如果指定的节点没有父节点则返回 null

    4.2 子节点

    1. parentNode.childNodes
    返回包含指定节点的子节点的集合,该集合为即时更新的集合
    返回值里面包含了所有的子节点,包括元素节点,文本节点等。不提倡使用childNodes,因为要做特殊处理
    1. var ul = document.querySelector('ul');
    2. for (var i = 0; i < ul.childNodes.length; i++) {
    3. if (ul.childNodes[i].nodeType == 1) {
    4. // ul.childNodes[i] 是元素节点
    5. console.log(ul.childNodes[i]);
    6. }
    7. }
  1. parentNode.children

是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 (这个是我们重点掌握的)。

其他子节点获取

  1. parentNode.firstChild
  2. parentNode.lastChild

找不到则返回null。会返回文本节点(空格之类)

其他子节点获取

  1. parentNode.firstElementChild
  2. parentNode.lastElementChild

返回第一个子元素节点和最后子元素节点,找不到则返回null。

其他方法

  1. parentNode.chilren[0]
  2. parentNode.chilren[parentNode.chilren.length- 1]

4.3 兄弟节点

  1. node.nextSibling
  2. node.previousSibling

返回当前元素的下一个兄弟元素节点和上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。

  1. node.nextElementSibling
  2. node.previousElementSibling

返回当前元素下一个兄弟元素节点和前一个,找不到则返回null

4.4 创建节点

动态创建元素节点。

  1. document.createElement('tagName')

4.5 添加节点

Web APIs - 图5

  1. node.appendChild(child)

将一个节点添加到指定父节点的子节点列表末尾

  1. node.insertBefore(child, 指定元素) // 参数1:创建的元素, 参数2:要插入指定的子节点

将一个节点添加到父节点的指定子节点前面

示例:

  1. <ul>
  2. <li>i</li>
  3. <li>i</li>
  4. <li>i</li>
  5. <li>i</li>
  6. </ul>
  7. <script>
  8. var newLi = document.createElement('li')
  9. newLi.innerHTML = 'one'
  10. var ul = document.querySelector('ul');
  11. ul.insertBefore(newLi, ul.children[0])
  12. </script>

image.png

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

image.png

4.6 删除节点

  1. node.removeChild(child)
  2. ul.removeChild(ul.children[3])

方法从 DOM 中删除一个子节点,返回删除的节点。

4.7 复制节点

  1. node.cloneNode()

方法返回调用该方法的节点的一个副本

  1. 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
  2. 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。

image.png

4.8 替换节点

  1. parentNode.replaceChild(newChild, oldChild);

4.9 三种创建元素区别

  • document.write()
  • element.innerHTML
  • document.createElement()

    1. <br />1. document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘<br />2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘<br />3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂<br />4. createElement() 创建多个元素效率稍低一点点,但是结构更清晰<br />
    1. var arr = [];
    2. for (var i = 0; i <= 100; i++) {
    3. arr.push('<a href="#">百度</a>');
    4. }
    5. inner.innerHTML = arr.join('');

    最快

5 事件

注册事件有两种方式:传统方式和方法监听注册方式

5.1 方法监听注册

  1. eventTarget.addEventListener(type, listener[, useCapture])

方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。

  • type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
  • listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数,是一个布尔值,默认是 false。参数是 true 那么在捕获阶段触发处理程序

    1. <br />IE8 及早期版本支持
    1. eventTarget.attachEvent(eventNameWithOn, callback)

    eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
    callback: 事件处理函数,当目标触发事件时回调函数被调用

    注册事件兼容性

    1. function addEventListener(element, eventName, fn) {
    2. // 判断当前浏览器是否支持 addEventListener 方法
    3. if (element.addEventListener) {
    4. element.addEventListener(eventName, fn); // 第三个参数 默认是false
    5. } else if (element.attachEvent) {
    6. element.attachEvent('on' + eventName, fn);
    7. } else {
    8. // 相当于 element.onclick = fn;
    9. element['on' + eventName] = fn;
    10. }
    11. }

    首先照顾大多数浏览器,再处理特殊浏览器

    5.2 删除事件

    ```javascript // 传统方式 eventTarget.onclick = null;

// 方法监听注册 eventTarget.removeEventListener(type, listener[, useCapture]); eventTarget.detachEvent(eventNameWithOn, callback);

  1. 兼容
  2. ```javascript
  3. function removeEventListener(element, eventName, fn) {
  4. // 判断当前浏览器是否支持 removeEventListener 方法
  5. if (element.removeEventListener) {
  6. element.removeEventListener(eventName, fn); // 第三个参数 默认是false
  7. } else if (element.detachEvent) {
  8. element.detachEvent('on' + eventName, fn);
  9. } else {
  10. element['on' + eventName] = null;
  11. }
  12. }

5.3 DOM事件流

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。

DOM 事件流分为3个阶段:
1. 捕获阶段
2. 当前目标阶段
3. 冒泡阶段

事件冒泡: IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
事件捕获: 网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。

通过事件冒泡传递信息, 也就是事件委托
image.png
image.png

些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave

5.4 事件对象

event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。它有很多属性和方法。

  1. eventTarget.onclick = function (event) { }
  2. eventTarget.addEventListener('click', function (event) { })

当我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。

兼容性

  1. var div = document.querySelector('div');
  2. div.onclick = function(e) {
  3. // 事件对象
  4. e = e || window.event;
  5. console.log(e);
  6. }

在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找
image.png

5.5 阻止默认行为

html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。form表单的默认提交

  1. a.onclick = function(e) {
  2. // 普通浏览器 e.preventDefault(); 方法
  3. e.preventDefault();
  4. // 低版本浏览器 ie678 returnValue 属性
  5. e.returnValue = false;
  6. // 我们可以利用return false 也能阻止默认行为 没有兼容性问题
  7. return false;
  8. }

5.6 阻止冒泡

  1. son.addEventListener('click', function(e) {
  2. if (e && e.stopPropagation) {
  3. e.stopPropagation(); // stop 停止 Propagation 传播
  4. } else {
  5. window.event.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
  6. }
  7. });

非标准写法:IE 6-8 利用事件对象 cancelBubble 属性

5.7 事件委托

给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
只操作了一次 DOM ,提高了程序的性能。

  1. var ul = document.querySelector('ul')
  2. ul.addEventListener('click', function(e) {
  3. console.log(e.target);
  4. })

5.8 常见鼠标事件

image.png
mouseenter 和mouseover的区别

  • 当鼠标移动到元素上时就会触发mouseenter 事件
  • 类似 mouseover,它们两者之间的差别是
  • mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发
  • 之所以这样,就是因为mouseenter不会冒泡
  • 跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡

5.9 禁止鼠标右键菜单

contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

  1. document.addEventListener('contextmenu', function (e) {
  2. e.preventDefault();
  3. })

相当于右键失效

5.10 禁止鼠标选中(selectstart 开始选中)

  1. document.addEventListener('selectstart', function(e) {
  2. e.preventDefault();
  3. })

5.11 鼠标事件对象

image.png

5.12 键盘事件

image.png
三个事件的执行顺序是: keydown — keypress —- keyup

5.13 键盘事件对象

image.png

  • onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
  • 在我们实际开发中,我们更多的使用keydown和keyup, 它能识别所有的键(包括功能键)
  • Keypress 不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值
    1. // 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
    2. document.addEventListener('keyup', function(e) {
    3. console.log('up:' + e.keyCode);
    4. // 我们可以利用keycode返回的ASCII码值来判断用户按下了那个键
    5. if (e.keyCode === 65) {
    6. alert('您按下的a键');
    7. } else {
    8. alert('您没有按下a键')
    9. }
    10. })

5.14 点击和删除函数

  1. var btn = document.querySelector('button')
  2. setTimeout(function(){
  3. btn.click()
  4. },3000)
  5. btn.onclick = function(){
  6. btn.remove()
  7. }

5.15 双击禁止选中文字

  1. window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

5.16 失去焦点函数

  1. input.onkeyup = function(e) {
  2. if (e.keyCode === 13) {
  3. // 手动调用表单失去焦点事件 不需要鼠标离开操作
  4. this.blur();
  5. }

5.17 表单处于选中

  1. var ipt = document.querySelector('input')
  2. ipt.select()

6 BOM

BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
image.png
BOM 比 DOM 更大,它包含 DOM。
image.png

6.1 顶级对象window

window 对象是浏览器的顶级对象,它具有双重角色

  • 它是 JS 访问浏览器窗口的一个接口。
  • 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。

在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。
注意:window下的一个特殊属性 window.name
image.png

6.2 页面(窗口)加载事件

  1. window.onload = function(){}
  2. // 或者
  3. window.addEventListener("load",function(){});

window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等), 就调用的处理函数。

  • window.onload 传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload 为准。
  • 如果使用 addEventListener 则没有限制

6.3 窗口加载事件

  1. document.addEventListener('DOMContentLoaded',function(){})

DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。

6.4 调整窗口大小事件

  1. window.onresize = function(){}
  2. window.addEventListener("resize",function(){});

window.onresize 是调整窗口大小加载事件, 当触发时就调用的处理函数。

注意:

  1. 只要窗口大小发生像素变化,就会触发这个事件。
  2. 我们经常利用这个事件完成响应式布局。 window.innerWidth 当前屏幕的宽度
  1. // 注册页面加载事件
  2. window.addEventListener('load', function() {
  3. var div = document.querySelector('div');
  4. // 注册调整窗口大小事件
  5. window.addEventListener('resize', function() {
  6. // window.innerWidth 获取窗口大小
  7. console.log('变化了');
  8. if (window.innerWidth <= 800) {
  9. div.style.display = 'none';
  10. } else {
  11. div.style.display = 'block';
  12. }
  13. })
  14. })

6.5 定时器

window 对象给我们提供了 2 个非常好用的方法-定时器。

  • setTimeout()
  • setInterval()

6.6 炸弹定时器setTimeout

  1. window.setTimeout(调用函数, [延迟的毫秒数]);

调用函数可以直接写函数,或者写函数名或者采取字符串‘函数名()’三种形式。第三种不推荐
延迟的毫秒数省略默认是 0,如果写,必须是毫秒

6.7 停止定时器clearTimeout

  1. window.clearTimeout(timeoutID)

示例:

  1. var btn = document.querySelector('button');
  2. var timer = setTimeout(function() {
  3. console.log('爆炸了');
  4. }, 5000);
  5. btn.addEventListener('click', function() {
  6. // 停止定时器
  7. clearTimeout(timer);
  8. })

6.8 闹钟定时器setInterval

  1. window.setInterval(回调函数, [间隔的毫秒数]);

重复调用一个函数,每隔这个时间,就去调用一次回调函数。

6.9 停止定时器clearInterval

  1. window.clearInterval(intervalID);

7 this指向问题

  1. 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
  2. 方法调用中谁调用this指向谁
  3. 构造函数中this指向构造函数的实例
    1. // 1. 全局作用域或者普通函数中this指向全局对象window( 注意定时器里面的this指向window)
    2. console.log(this);
    3. function fn() {
    4. console.log(this);
    5. }
    6. window.fn();
    7. window.setTimeout(function() {
    8. console.log(this);
    9. }, 1000);
    10. // 2. 方法调用中谁调用this指向谁
    11. var o = {
    12. sayHi: function() {
    13. console.log(this); // this指向的是 o 这个对象
    14. }
    15. }
    16. o.sayHi();
    17. var btn = document.querySelector('button');
    18. btn.addEventListener('click', function() {
    19. console.log(this); // 事件处理函数中的this指向的是btn这个按钮对象
    20. })
    21. // 3. 构造函数中this指向构造函数的实例
    22. function Fun() {
    23. console.log(this); // this 指向的是fun 实例对象
    24. }
    25. 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 事件循环

    image.png
    image.png

由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。

9 location 对象

window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为 这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。

9.1 URL

统一资源定位符 (Uniform Resource Locator, URL) 是互联网上标准资源的地址。互联网上的每个文件都有 一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

  1. protocol://host[:port]/path/[?query]#fragment
  2. http://www.itcast.cn/index.html?name=andy&age=18#link

image.png

9.2 location 对象的属性

image.png

9.3 location对象的常见方法

image.png

  1. // 记录浏览历史,所以可以实现后退功能
  2. location.assign('http://www.baidu.com');
  3. // 不记录浏览历史,所以不可以实现后退功能
  4. location.replace('http://www.baidu.com');
  5. location.reload(true);

10 navigator 对象

navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客 户机发送服务器的 user-agent 头部的值。

  1. 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))) {
  2. window.location.href = ""; //手机
  3. } else {
  4. window.location.href = ""; //电脑
  5. }

11 history对象

window对象给我们提供了一个 history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。
image.png

  1. history.go(-1) // 返回上一页

12 元素偏移量offset 系列

offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。

  1. 获得元素距离带有定位父元素的位置
  2. 获得元素自身的大小(宽度高度)
  3. 注意:返回的数值都不带单位

image.png
image.png

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 获取鼠标在盒子内的坐标

  1. 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
  2. 首先得到鼠标在页面中的坐标(e.pageX, e.pageY)
  3. 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
  4. 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标
  5. 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动

    1. var box = document.querySelector('.box');
    2. box.addEventListener('mousemove', function(e) {
    3. var x = e.pageX - this.offsetLeft;
    4. var y = e.pageY - this.offsetTop;
    5. this.innerHTML = 'x坐标是' + x + ' y坐标是' + y;
    6. })

    12.3 移动盒子案例

    1. // 按下鼠标
    2. title.addEventListener('mousedown', (e) => {
    3. // 得到鼠标在页面中的坐标(e.pageX, e.pageY)
    4. // 得到盒子在页面中的距离 ( login.offsetLeft, login.offsetTop)
    5. // 求出鼠标在盒子内的坐标
    6. var mou_x = e.pageX - login.offsetLeft
    7. var mou_y = e.pageY - login.offsetTop
    8. // 鼠标移动
    9. document.addEventListener('mousemove', move)
    10. // 盒子移动
    11. function move(e) {
    12. // 鼠标在页面坐标 - 鼠标在盒子里的坐标 = 盒子的坐标
    13. login.style.left = e.pageX - mou_x + 'px'
    14. login.style.top = e.pageY - mou_y + 'px'
    15. }
    16. document.addEventListener('mouseup', () => {
    17. // 放开鼠标,清除事件
    18. document.removeEventListener('mousemove', move)
    19. })
    20. })

    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’ })

  1. ![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)
  2. <a name="BMtk3"></a>
  3. # 13 元素可视区 client 系列
  4. 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)
  5. <a name="n2omp"></a>
  6. # 14 立即执行函数
  7. 立即执行函数 `(function() {})()` 或者 `(function(){}())`<br />主要作用: 创建一个独立的作用域。 避免了命名冲突问题
  8. 可以看作匿名函数自调用<br />
  9. <a name="RbBBa"></a>
  10. # 15 物理像素比与字体大小应用
  11. ```javascript
  12. // 获取的html 的根元素
  13. var docEl = document.documentElement
  14. // dpr 物理像素比
  15. var dpr = window.devicePixelRatio || 1
  16. // adjust body font size 设置我们body 的字体大小
  17. function setBodyFontSize() {
  18. // 如果页面中有body 这个元素 就设置body的字体大小
  19. if (document.body) {
  20. document.body.style.fontSize = (12 * dpr) + 'px'
  21. } else {
  22. // 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body
  23. // 的字体大小
  24. document.addEventListener('DOMContentLoaded', setBodyFontSize)
  25. }
  26. }
  27. setBodyFontSize();
  28. // set 1rem = viewWidth / 10 设置我们html 元素的文字大小
  29. function setRemUnit() {
  30. var rem = docEl.clientWidth / 10
  31. docEl.style.fontSize = rem + 'px'
  32. }
  33. setRemUnit()
  34. // reset rem unit on page resize 当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
  35. window.addEventListener('resize', setRemUnit)
  36. // pageshow 是我们重新加载页面触发的事件
  37. window.addEventListener('pageshow', function(e) {
  38. // e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
  39. if (e.persisted) {
  40. setRemUnit()
  41. }
  42. })

在重新加载页面中,pageshow会在load事件触发后触发;

16 元素滚动scroll 系列属性

image.png
scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。
image.png

16.1 元素被卷去头部和页面被卷去头部区别

页面被卷去的头部:可以通过window.pageYOffset 获得
元素被卷去的头部是 element.scrollTop

16.2 滚动页面事件

滚动条在滚动时会触发 onscroll 事件。
固定侧边栏案例:

  1. // 原始距离顶部大小
  2. var bannnerTop = banner.offsetTop
  3. // 侧边栏距离banner顶部的距离
  4. var sliderTop = slider.offsetTop - bannnerTop
  5. document.addEventListener('scroll', ()=>{
  6. if( window.pageYOffset >= bannnerTop){
  7. slider.style.position = 'fixed'
  8. slider.style.top = sliderTop + 'px'
  9. slider.children[0].style.display = 'block'
  10. } else {
  11. slider.style.position = 'absolute'
  12. slider.style.top = 300 + 'px'
  13. slider.children[0].style.display = 'none'
  14. }
  15. })

16.3 页面被卷去的头部兼容性解决方案

  1. function getScroll() {
  2. return {
  3. left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
  4. top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
  5. };
  6. }
  7. // 使用的时候
  8. getScroll().left

17 三大系列区别

image.png
1.offset系列 经常用于获得元素位置 offsetLeft offsetTop
2.client经常用于获取元素大小 clientWidth clientHeight
3.scroll 经常用于获取滚动距离 scrollTop scrollLeft
4.注意页面滚动的距离通过 window.pageXOffset 获得

18 动画封装

  1. function animate(obj, target) {
  2. // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
  3. // 解决方案就是 让我们元素只有一个定时器执行
  4. // 先清除以前的定时器,只保留当前的一个定时器执行
  5. clearInterval(obj.timer);
  6. obj.timer = setInterval(function() {
  7. if (obj.offsetLeft >= target) {
  8. // 停止动画 本质是停止定时器
  9. clearInterval(obj.timer);
  10. }
  11. obj.style.left = obj.offsetLeft + 1 + 'px';
  12. }, 30);
  13. }

18.1 缓慢动画效果

  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
  2. 核心算法: (目标值 - 现在的位置) / 10 做为每次移动的距离步长
  3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
  4. 注意步长值需要取整
  5. 如果是正值,则步长 往大了取整,如果是负值,则步长向小了取整

    1. function animate(obj, target, callback) {
    2. // 先清除以前的定时器,只保留当前的一个定时器执行
    3. clearInterval(obj.timer);
    4. obj.timer = setInterval(function() {
    5. // 步长值写到定时器的里面
    6. // 把我们步长值改为整数 不要出现小数的问题
    7. var step = (target - obj.offsetLeft) / 10;
    8. // 如果是正值,则步长 往大了取整,如果是负值,则步长向小了取整
    9. step = step > 0 ? Math.ceil(step) : Math.floor(step);
    10. if (obj.offsetLeft == target) {
    11. // 停止动画 本质是停止定时器
    12. clearInterval(obj.timer);
    13. // 回调函数写到定时器结束里面
    14. callback && callback();
    15. }
    16. // 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
    17. obj.style.left = obj.offsetLeft + step + 'px';
    18. }, 15);
    19. }

    Math.ceil() // 向上取整
    Math.floor() // 向下取整

18.2 页面滚动

  1. window.scroll(x,y)
  2. window.scroll(0, 0);

返回顶部案例:

  1. // 点击返回顶部
  2. var goBack = document.querySelector('.goBack')
  3. goBack.addEventListener('click', function () {
  4. animation(window, 0)
  5. })
  6. function animation(obj, target, callback) {
  7. clearInterval(obj.timer)
  8. obj.timer = setInterval(() => {
  9. // 向上取整,不要有小数影响
  10. var step = (target - obj.pageYOffset) / 10
  11. step = step > 0 ? Math.ceil(step) : Math.floor(step);
  12. if (obj.pageYOffset == target) {
  13. clearInterval(obj.timer)
  14. callback && callback();
  15. }
  16. obj.scroll(0, obj.pageYOffset + step)
  17. }, 15);
  18. }

19 节流阀

防止按钮连续点击造成播放过快
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
如:
开始设置一个变量var flag= true;
if(flag){flag = false; do something} 关闭水龙头
利用回调函数动画执行完毕, flag = true 打开水龙头

  1. // 截流阀,用于等一张图片切换完再打开
  2. var flag = true
  3. // 点击右键滚动
  4. arrow_r.addEventListener('click', function () {
  5. if(flag) {
  6. flag = false // 关闭水龙头
  7. if (num == ul.children.length - 1) {
  8. num = 0
  9. ul.style.left = 0
  10. }
  11. num++
  12. animate(ul, -num * focusWidth, ()=>{
  13. flag = true // 开启水龙头
  14. })
  15. // 小圆点跟随图片动
  16. circle_num++
  17. if (circle_num == circle.children.length) {
  18. circle_num = 0
  19. }
  20. circleChange()
  21. }
  22. })

20 触屏事件

  • 移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。
  • 比如触屏事件 touch(也称触摸事件),Android和 IOS 都有。
  • touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。
  • 触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。

image.png

20.1 触摸事件对象(TouchEvent)

TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件
这类事件用于描述一个或多个触点,使开发者可以检测触点的移动触点的增加和减少,等等
image.png
因为平时我们都是给元素注册触摸事件,所以重点记住 ``**targetTocuhes**

20.2 移动端拖动元素案例

  • touchstart、touchmove、touchend可以实现拖动元素
  • 拖动元素需要当前手指的坐标值 ,使用 targetTouches[0] 里面的pageX 和 pageY
  • 盒子原来的位置 + 手指移动的距离

    1. var div = document.querySelector('div')
    2. // 初始位置
    3. var start_x = 0
    4. var start_y = 0
    5. // 盒子原来的坐标
    6. var box_x = 0
    7. var box_y = 0
    8. div.addEventListener('touchstart', function (e) {
    9. // 当前手指坐标值
    10. start_x = e.targetTouches[0].pageX
    11. start_y = e.targetTouches[0].pageY
    12. // 盒子位置
    13. box_x = this.offsetLeft
    14. box_y = this.offsetTop
    15. console.log('touchstart');
    16. })
    17. div.addEventListener('touchmove', function (e) {
    18. // 计算手指的移动距离
    19. var move_x = e.targetTouches[0].pageX - start_x
    20. var move_y = e.targetTouches[0].pageY - start_y
    21. // 盒子移动的距离
    22. this.style.left = box_x + move_x + 'px'
    23. this.style.top = box_y + move_y + 'px'
    24. // 阻止屏幕滚动的默认行为
    25. e.preventDefault()
    26. console.log('touchmove');
    27. })

    仿pc端写法:

    1. var div = document.querySelector('div')
    2. div.addEventListener('touchstart', function (e) {
    3. // 鼠标在盒子的位置
    4. var mou_x = e.targetTouches[0].pageX - this.offsetLeft
    5. var mou_y = e.targetTouches[0].pageY - this.offsetTop
    6. document.addEventListener('touchmove', move)
    7. function move(e) {
    8. // 手指移动变化的位置 - 手指在盒子的位置 = 盒子变化的位置
    9. div.style.left = e.targetTouches[0].pageX - mou_x + 'px'
    10. div.style.top = e.targetTouches[0].pageY - mou_y + 'px'
    11. // 阻止屏幕滚动的默认行为
    12. e.preventDefault()
    13. }
    14. document.addEventListener('touchend', () => {
    15. // 放开鼠标,清除事件
    16. document.removeEventListener('touchmove', move)
    17. })
    18. })

    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’);

  1. <a name="6ZxuH"></a>
  2. # 22 移动端click延时
  3. 移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。<br />解决方案:<br />1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟。
  4. ```html
  5. <meta name="viewport" content="user-scalable=no">

使用插件。 fastclick 插件解决 300ms 延迟。
GitHub官网地址: https://github.com/ftlabs/fastclick

  1. if ('addEventListener' in document) {
  2. document.addEventListener('DOMContentLoaded', function() {
  3. FastClick.attach(document.body);
  4. }, false);
  5. }

23 本地存储

  • 数据存储在用户浏览器中
  • 容量较大,sessionStorage约5M、localStorage约20M
  • 只能存储字符串,可以将对象JSON.stringify() 编码后存储

    23.1 sessionStorage

    1、生命周期为关闭浏览器窗口
    2、在同一个窗口(页面)下数据可以共享
    3、以键值对的形式存储使用
  1. // 存储数据:
  2. sessionStorage.setItem(key, value)
  3. // 获取数据
  4. sessionStorage.getItem(key)
  5. // 删除数据
  6. sessionStorage.removeItem(key)
  7. // 清空数据:(所有都清除掉)
  8. sessionStorage.clear()

23.2 localStorage

  • 声明周期永久生效,除非手动删除 否则关闭页面也会存在
  • 可以多窗口(页面)共享(同一浏览器可以共享)
  • 以键值对的形式存储使用 ```javascript // 存储数据 localStorage.setItem(key, value)

// 获取数据 localStorage.getItem(key)

// 删除数据 localStorage.removeItem(key)

// 清空数据 localStorage.clear() ```