介绍
知道 ECMAScript 与 JavaScript 的关系,Web APIs 是浏览器扩展的功能。
严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。
ECMAScript 运行在浏览器中然后再结合 Web APIs 才是真正的 JavaScript,Web APIs 的核心是 DOM 和 BOM。
扩展阅读:ECMAScript 规范在不断的更新中,存在多个不同的版本,早期的版本号采用数字顺序编号如 ECMAScript3、ECMAScript5,后来由于更新速度较快便采用年份做为版本号,如 ECMAScript2017、ECMAScript2018 这种格式,ECMAScript6 是 2015 年发布的,常叫做 EMCAScript2015。
关于 JavaScript 历史的扩展阅读。
知道 DOM 相关的概念,建立对 DOM 的初步认识,学习 DOM 的基本操作,体会 DOM 的作用
DOM(Document Object Model)是将整个 HTML 文档的每一个标签元素视为一个对象,这个对象下包含了许多的属性和方法,通过操作这些属性或者调用这些方法实现对 HTML 的动态更新,为实现网页特效以及用户交互提供技术支撑。
简言之 DOM 是用来动态修改 HTML 的,其目的是开发网页特效及用户交互。
观察一个小例子:
上述的例子中当用户分分别点击【开始】或【结束】按钮后,通过右侧调试窗口可以观察到 html 标签的内容在不断的发生改变,这便是通过 DOM 实现的。
概念
DOM 树
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>标题</title> </head> <body> 文本 <a href="">链接名</a> <div id="" class="">文本</div> </body> </html>
|
如下图所示,将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树,文档树直观的体现了标签与标签之间的关系。
DOM 节点
节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为元素节点、属性节点、文本节点等。
- 【元素节点】其实就是 HTML 标签,如上图中
head
、div
、body
等都属于元素节点。
- 【属性节点】是指 HTML 标签中的属性,如上图中
a
标签的 href
属性、div
标签的 class
属性。
- 【文本节点】是指 HTML 标签的文字内容,如
title
标签中的文字。
- 【根节点】特指
html
标签。
- 其它…
document
document
是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法,document
是学习 DOM 的核心。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script>
console.log(document.documentElement);
console.log(document.body);
document.write('Hello World!'); </script>
|
上述列举了 document
对象的部分属性和方法,我们先对 document
有一个整体的认识。
获取DOM对象
- querySelector 满足条件的第一个元素
- querySelectorAll 满足条件的元素集合 返回伪数组
- 了解其他方式
- getElementById
- getElementsByTagName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DOM - 查找节点</title> </head> <body> <h3>查找元素类型节点</h3> <p>从整个 DOM 树中查找 DOM 节点是学习 DOM 的第一个步骤。</p> <ul> <li>元素</li> <li>元素</li> <li>元素</li> <li>元素</li> </ul> <script> const p = document.querySelector('p') const lis = document.querySelectorAll('li') </script> </body> </html>
|
总结:
- document.getElementById 专门获取元素类型节点,根据标签的
id
属性查找
- 任意 DOM 对象都包含 nodeType 属性,用来检检测节点类型
操作元素内容
通过修改 DOM 的文本内容,动态改变网页的内容。
innerText
将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析。
1 2 3 4 5 6
| <script> const intro = document.querySelector('.intro') </script>
|
innerHTML
将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。
1 2 3 4 5 6
| <script> const intro = document.querySelector('.intro') intro.innerHTML = '嗨~ 我叫韩梅梅!' intro.innerHTML = '<h4>嗨~ 我叫韩梅梅!</h4>' </script>
|
总结:如果文本内容中包含 html
标签时推荐使用 innerHTML
,否则建议使用 innerText
属性。
##操作元素属性
有3种方式可以实现对属性的修改:
常用属性修改
- 直接能过属性名修改,最简洁的语法
1 2 3 4 5 6 7 8
| <script> const pic = document.querySelector('.pic') pic.src = './images/lion.webp' pic.width = 400; pic.alt = '图片不见了...' </script>
|
控制样式属性
- 应用【修改样式】,通过修改行内样式
style
属性,实现对样式的动态修改。
通过元素节点获得的 style
属性本身的数据类型也是对象,如 box.style.color
、box.style.width
分别用来获取元素节点 CSS 样式的 color
和 width
的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>练习 - 修改样式</title> </head> <body> <div class="box">随便一些文本内容</div> <script> const box = document.querySelector('.intro') box.style.color = 'red' box.style.width = '300px' box.style.backgroundColor = 'pink' </script> </body> </html>
|
任何标签都有 style
属性,通过 style
属性可以动态更改网页标签的样式,如要遇到 css
属性中包含字符 -
时,要将 -
去掉并将其后面的字母改成大写,如 background-color
要写成 box.style.backgroundColor
- 操作类名(className) 操作CSS
如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>练习 - 修改样式</title> <style> .pink { background: pink; color: hotpink; } </style> </head> <body> <div class="box">随便一些文本内容</div> <script> const box = document.querySelector('.intro') box.className = 'pink' </script> </body> </html>
|
注意:
1.由于class是关键字, 所以使用className去代替
2.className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名
- 通过 classList 操作类控制CSS
为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div { width: 200px; height: 200px; background-color: pink; }
.active { width: 300px; height: 300px; background-color: hotpink; margin-left: 100px; } </style> </head>
<body>
<div class="one"></div> <script> let box = document.querySelector('div') box.classList.toggle('one') </script> </body>
</html>
|
操作表单元素属性
表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框
正常的有属性有取值的跟其他的标签属性没有任何区别
获取:DOM对象.属性名
设置:DOM对象.属性名= 新值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title>
</head>
<body> <input type="text" value="请输入"> <button disabled>按钮</button> <input type="checkbox" name="" id="" class="agree"> <script> let input = document.querySelector('input') input.value = '小米手机' input.type = 'password'
let btn = document.querySelector('button') btn.disabled = false let checkbox = document.querySelector('.agree') checkbox.checked = false </script> </body>
</html>
|
自定义属性
标准属性: 标签天生自带的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、selected
自定义属性:
在html5中推出来了专门的data-自定义属性
在标签上一律以data-开头
在DOM对象上一律以dataset对象方式获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title>
</head>
<body> <div data-id="1"> 自定义属性 </div> <script> let div = document.querySelector('div') console.log(div.dataset.id) </script> </body>
</html>
|
间歇函数
知道间歇函数的作用,利用间歇函数创建定时任务。
setInterval
是 JavaScript 中内置的函数,它的作用是间隔固定的时间自动重复执行另一个函数,也叫定时器函数。
1 2 3 4 5 6 7 8 9 10
| <script> function repeat() { console.log('不知疲倦的执行下去....') }
setInterval(repeat, 1000) </script>
|
今日单词
单词 |
说明 |
解释 |
setInterval |
定时器 |
setInterval(repeat, 1000) |
事件
事件是编程语言中的术语,它是用来描述程序的行为或状态的,一旦行为或状态发生改变,便立即调用一个函数。
例如:用户使用【鼠标点击】网页中的一个按钮、用户使用【鼠标拖拽】网页中的一张图片
事件监听
结合 DOM 使用事件时,需要为 DOM 对象添加事件监听,等待事件发生(触发)时,便立即调用一个函数。
addEventListener
是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和【事件回调】。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>事件监听</title> </head> <body> <h3>事件监听</h3> <p id="text">为 DOM 元素添加事件监听,等待事件发生,便立即执行一个函数。</p> <button id="btn">点击改变文字颜色</button> <script> const btn = document.querySelector('#btn')
btn.addEventListener('click', function () { console.log('等待事件被触发...') let text = document.getElementById('text') text.style.color = 'red' })
</script> </body> </html>
|
完成事件监听分成3个步骤:
- 获取 DOM 元素
- 通过
addEventListener
方法为 DOM 节点添加事件监听
- 等待事件触发,如用户点击了某个按钮时便会触发
click
事件类型
- 事件触发后,相对应的回调函数会被执行
大白话描述:所谓的事件无非就是找个机会(事件触发)调用一个函数(回调函数)。
事件类型
1 2 3 4 5 6 7 8 9 10 11 12
| <script> btn.addEventListener('dblclick', function () { console.log('等待事件被触发...'); const text = document.querySelector('.text') text.style.color = 'red' })
</script>
|
结论:【事件类型】决定了事件被触发的方式,如 click
代表鼠标单击,dblclick
代表鼠标双击。
事件处理程序
addEventListener
的第2个参数是函数,这个函数会在事件被触发时立即被调用,在这个函数中可以编写任意逻辑的代码,如改变 DOM 文本颜色、文本内容等。
1 2 3 4 5 6 7 8 9 10 11 12
| <script> btn.addEventListener('dblclick', function () { console.log('等待事件被触发...') const text = document.querySelector('.text') text.style.color = 'red' text.style.fontSize = '20px' }) </script>
|
结论:【事件处理程序】决定了事件触发后应该执行的逻辑。
事件类型
将众多的事件类型分类可分为:鼠标事件、键盘事件、表单事件、焦点事件等,我们逐一展开学习。
鼠标事件
鼠标事件是指跟鼠标操作相关的事件,如单击、双击、移动等。
- `mouseenter 监听鼠标是否移入 DOM 元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <h3>鼠标事件</h3> <p>监听与鼠标相关的操作</p> <hr> <div class="box"></div> <script> const box = document.querySelector('.box');
box.addEventListener('mouseenter', function () { this.innerText = '鼠标移入了...'; this.style.cursor = 'move'; }) </script> </body>
|
- `mouseleave 监听鼠标是否移出 DOM 元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <h3>鼠标事件</h3> <p>监听与鼠标相关的操作</p> <hr> <div class="box"></div> <script> const box = document.querySelector('.box');
box.addEventListener('mouseleave', function () { this.innerText = '鼠标移出了...'; }) </script> </body>
|
键盘事件
keydown 键盘按下触发
keyup 键盘抬起触发
焦点事件
focus 获得焦点
blur 失去焦点
文本框输入事件
input
事件对象
任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <h3>事件对象</h3> <p>任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。</p> <hr> <div class="box"></div> <script> const box = document.querySelector('.box')
box.addEventListener('click', function (e) { console.log('任意事件类型被触发后,相关信息会以对象形式被记录下来...');
console.log(e) }) </script> </body>
|
事件回调函数的【第1个参数】即所谓的事件对象,通常习惯性的将这个对数命名为 event
、ev
、ev
。
接下来简单看一下事件对象中包含了哪些有用的信息:
ev.type
当前事件的类型
ev.clientX/Y
光标相对浏览器窗口的位置
ev.offsetX/Y
光标相于当前 DOM 元素的位置
注:在事件回调函数内部通过 window.event 同样可以获取事件对象。
环境对象
能够分析判断函数运行在不同环境中 this 所指代的对象。
环境对象指的是函数内部特殊的变量 this
,它代表着当前函数运行时所处的环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <script> function sayHi() { console.log(this); }
let user = { name: '张三', sayHi: sayHi } let person = { name: '李四', sayHi: sayHi }
sayHi() window.sayHi()
user.sayHi() person.sayHi() </script>
|
结论:
this
本质上是一个变量,数据类型为对象
- 函数的调用方式不同
this
变量的值也不同
- 【谁调用
this
就是谁】是判断 this
值的粗略规则
- 函数直接调用时实际上
window.sayHi()
所以 this
的值为 window
回调函数
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script> function foo(arg) { console.log(arg); }
foo(10); foo('hello world!'); foo(['html', 'css', 'javascript']);
function bar() { console.log('函数也能当参数...'); } foo(bar); </script>
|
函数 bar
做参数传给了 foo
函数,bar
就是所谓的回调函数了!!!
我们回顾一下间歇函数 setInterval
1 2 3 4 5 6 7
| <script> function fn() { console.log('我是回调函数...'); } setInterval(fn, 1000); </script>
|
fn
函数做为参数传给了 setInterval
,这便是回调函数的实际应用了,结合刚刚学习的函数表达式上述代码还有另一种更常见写法。
1 2 3 4 5 6
| <script> setInterval(function () { console.log('我是回调函数...'); }, 1000); </script>
|
结论:
- 回调函数本质还是函数,只不过把它当成参数使用
- 使用匿名函数做为回调函数比较常见
事件流
事件流是对事件执行过程的描述,了解事件的执行过程有助于加深对事件的理解,提升开发实践中对事件运用的灵活度。
如上图所示,任意事件被触发时总会经历两个阶段:【捕获阶段】和【冒泡阶段】。
简言之,捕获阶段是【从父到子】的传导过程,冒泡阶段是【从子向父】的传导过程。
捕获和冒泡
了解了什么是事件流之后,我们来看事件流是如何影响事件执行的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <body> <h3>事件流</h3> <p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p> <div class="outer"> <div class="inner"> <div class="child"></div> </div> </div> <script> const outer = document.querySelector('.outer'); const inner = document.querySelector('.inner'); const child = document.querySelector('.child'); document.documentElement.addEventListener('click', function () { console.log('html...') }) document.body.addEventListener('click', function () { console.log('body...') })
outer.addEventListener('click', function () { console.log('outer...') }) outer.addEventListener('click', function () { console.log('inner...') }) outer.addEventListener('click', function () { console.log('child...') }) </script> </body>
|
执行上述代码后发现,当单击事件触发时,其祖先元素的单击事件也【相继触发】,这是为什么呢?
结合事件流的特征,我们知道当某个元素的事件被触发时,事件总是会先经过其祖先才能到达当前元素,然后再由当前元素向祖先传递,事件在流动的过程中遇到相同的事件便会被触发。
再来关注一个细节就是事件相继触发的【执行顺序】,事件的执行顺序是可控制的,即可以在捕获阶段被执行,也可以在冒泡阶段被执行。
如果事件是在冒泡阶段执行的,我们称为冒泡模式,它会先执行子盒子事件再去执行父盒子事件,默认是冒泡模式。
如果事件是在捕获阶段执行的,我们称为捕获模式,它会先执行父盒子事件再去执行子盒子事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <h3>事件流</h3> <p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p> <div class="outer"> <div class="inner"></div> </div> <script> const outer = document.querySelector('.outer') const inner = document.querySelector('.inner')
outer.addEventListener('click', function () { console.log('outer...') }, true) outer.addEventListener('click', function () { console.log('inner...') }, true) </script> </body>
|
结论:
addEventListener
第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发
addEventListener
第3个参数为 true
表示捕获阶段触发,false
表示冒泡阶段触发,默认值为 false
- 事件流只会在父子元素具有相同事件类型时才会产生影响
- 绝大部分场景都采用默认的冒泡模式(其中一个原因是早期 IE 不支持捕获)
阻止冒泡
阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <body> <h3>阻止冒泡</h3> <p>阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。</p> <div class="outer"> <div class="inner"> <div class="child"></div> </div> </div> <script> const outer = document.querySelector('.outer') const inner = document.querySelector('.inner') const child = document.querySelector('.child')
outer.addEventListener('click', function () { console.log('outer...') })
inner.addEventListener('click', function (ev) { console.log('inner...')
ev.stopPropagation() })
child.addEventListener('click', function (ev) { console.log('child...')
ev.stopPropagation() }) </script> </body>
|
结论:事件对象中的 ev.stopPropagation
方法,专门用来阻止事件冒泡。
鼠标经过事件:
mouseover 和 mouseout 会有冒泡效果
mouseenter 和 mouseleave 没有冒泡效果 (推荐)
事件委托
事件委托是利用事件流的特征解决一些现实开发需求的知识技巧,主要的作用是提升程序效率。
大量的事件监听是比较耗费性能的,如下代码所示
1 2 3 4 5 6 7 8 9 10 11
| <script> const buttons = document.querySelectorAll('table button');
for(let i = 0; i <= buttons.length; i++) { buttons.addEventListener('click', function () { }) } </script>
|
利用事件流的特征,可以对上述的代码进行优化,事件的的冒泡模式总是会将事件流向其父元素的,如果父元素监听了相同的事件类型,那么父元素的事件就会被触发并执行,正是利用这一特征对上述代码进行优化,如下代码所示:
1 2 3 4 5 6 7 8 9 10
| <script> let buttons = document.querySelectorAll('table button'); let parents = document.querySelector('table'); parents.addEventListener('click', function () { console.log('点击任意子元素都会触发事件...'); }) </script>
|
我们的最终目的是保证只有点击 button 子元素才去执行事件的回调函数,如何判断用户点击是哪一个子元素呢?
事件对象中的属性 target
或 srcElement
属性表示真正触发事件的元素,它是一个元素类型的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> const buttons = document.querySelectorAll('table button') const parents = document.querySelector('table') parents.addEventListener('click', function (ev) { if(ev.target.tagName === 'BUTTON') { } }) </script>
|
优化过的代码只对祖先元素添加事件监听,相比对 10000 个元素添加事件监听执行效率要高许多!!!
其他事件
页面加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
有些时候需要等页面资源全部处理完了做一些事情
事件名:load
监听页面所有资源加载完毕:
1 2 3
| window.addEventListener('load', function() { })
|
元素滚动事件
滚动条在滚动的时候持续触发的事件
1 2 3
| window.addEventListener('scroll', function() { })
|
页面尺寸事件
会在窗口尺寸改变的时候触发事件:
1 2 3
| window.addEventListener('resize', function() { })
|
元素尺寸与位置
获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetWidth和offsetHeight
获取出来的是数值,方便计算
注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
日期对象
掌握 Date 日期对象的使用,动态获取当前计算机的时间。
ECMAScript 中内置了获取系统时间的对象 Date,使用 Date 时与之前学习的内置对象 console 和 Math 不同,它需要借助 new 关键字才能使用。
实例化
1 2 3 4 5 6
|
const date = new Date('2020-05-01')
console.log(typeof date)
|
方法
1 2 3 4 5 6
| const date = new Date();
const year = date.getFullYear(); const month = date.getMonth();
|
getFullYear 获取四位年份
getMonth 获取月份,取值为 0 ~ 11
getDate 获取月份中的每一天,不同月份取值也不相同
getDay 获取星期,取值为 0 ~ 6
getHours 获取小时,取值为 0 ~ 23
getMinutes 获取分钟,取值为 0 ~ 59
getSeconds 获取秒,取值为 0 ~ 59
时间戳
时间戳是指1970年01月01日00时00分00秒起至现在的总秒数或毫秒数,它是一种特殊的计量时间的方式。
注:ECMAScript 中时间戳是以毫秒计的。
1 2 3 4 5 6 7 8
| const date = new Date()
console.log(date.getTime())
console.log(+new Date())
console.log(Date.now())
|
获取时间戳的方法,分别为 getTime 和 Date.now 和 +new Date()
DOM 节点
掌握元素节点创建、复制、插入、删除等操作的方法,能够依据元素节点的结构关系查找节点
回顾之前 DOM 的操作都是针对元素节点的属性或文本的,除此之外也有专门针对元素节点本身的操作,如插入、复制、删除、替换等。
插入节点
在已有的 DOM 节点中插入新的 DOM 节点时,需要关注两个关键因素:首先要得到新的 DOM 节点,其次在哪个位置插入这个节点。
如下代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <body> <h3>插入节点</h3> <p>在现有 dom 结构基础上插入新的元素节点</p> <hr> <div class="box"></div> <button class="btn">插入节点</button> <script> const btn = document.querySelector('.btn') btn.addEventListener('click', function () { const p = document.createElement('p') p.innerText = '创建的新的p标签' p.className = 'info' const p2 = document.querySelector('p').cloneNode(true) p2.style.color = 'red'
document.querySelector('.box').appendChild(p) document.querySelector('.box').appendChild(p2) }) </script> </body>
|
结论:
createElement
动态创建任意 DOM 节点
cloneNode
复制现有的 DOM 节点,传入参数 true 会复制所有子节点
appendChild
在末尾(结束标签前)插入节点
再来看另一种情形的代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <body> <h3>插入节点</h3> <p>在现有 dom 结构基础上插入新的元素节点</p> <hr> <button class="btn1">在任意节点前插入</button> <ul> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> <script> const btn1 = document.querySelector('.btn1') btn1.addEventListener('click', function () {
const relative = document.querySelector('li:nth-child(2)')
const li1 = document.createElement('li') li1.style.color = 'red' li1.innerText = 'Web APIs'
const li2 = document.querySelector('li:first-child').cloneNode(true) li2.style.color = 'blue'
document.querySelector('ul').insertBefore(li1, relative) document.querySelector('ul').insertBefore(li2, relative) }) </script> </body>
|
结论:
createElement
动态创建任意 DOM 节点
cloneNode
复制现有的 DOM 节点,传入参数 true 会复制所有子节点
insertBefore
在父节点中任意子节点之前插入新节点
删除节点
删除现有的 DOM 节点,也需要关注两个因素:首先由父节点删除子节点,其次是要删除哪个子节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <button>删除节点</button> <ul> <li>HTML</li> <li>CSS</li> <li>Web APIs</li> </ul>
<script> const btn = document.querySelector('button') btn.addEventListener('click', function () { let ul = document.querySelector('ul') let lis = document.querySelectorAll('li')
ul.removeChild(lis[0]) }) </script> </body>
|
结论:removeChild
删除节点时一定是由父子关系。
查找节点
DOM 树中的任意节点都不是孤立存在的,它们要么是父子关系,要么是兄弟关系,不仅如此,我们可以依据节点之间的关系查找节点。
父子关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <button class="btn1">所有的子节点</button> <ul> <li>HTML</li> <li>CSS</li> <li>JavaScript 基础</li> <li>Web APIs</li> </ul> <script> const btn1 = document.querySelector('.btn1') btn1.addEventListener('click', function () { const ul = document.querySelector('ul')
console.log(ul.childNodes) console.log(ul.children) }) </script> </body>
|
结论:
childNodes
获取全部的子节点,回车换行会被认为是空白文本节点
children
只获取元素类型节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <body> <table> <tr> <td width="60">序号</td> <td>课程名</td> <td>难度</td> <td width="80">操作</td> </tr> <tr> <td>1</td> <td><span>HTML</span></td> <td>初级</td> <td><button>变色</button></td> </tr> <tr> <td>2</td> <td><span>CSS</span></td> <td>初级</td> <td><button>变色</button></td> </tr> <tr> <td>3</td> <td><span>Web APIs</span></td> <td>中级</td> <td><button>变色</button></td> </tr> </table> <script> const buttons = document.querySelectorAll('table button') for(let i = 0; i < buttons.length; i++) { buttons[i].addEventListener('click', function () { this.parentNode.parentNode.style.color = 'red' }) } </script> </body>
|
结论:parentNode
获取父节点,以相对位置查找节点,实际应用中非常灵活。
兄弟关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <ul> <li>HTML</li> <li>CSS</li> <li>JavaScript 基础</li> <li>Web APIs</li> </ul> <script> const lis = document.querySelectorAll('ul li')
for(let i = 0; i < lis.length; i++) { lis[i].addEventListener('click', function () { console.log(this.previousSibling) console.log(this.nextSibling) }) } </script> </body>
|
结论:
previousSibling
获取前一个节点,以相对位置查找节点,实际应用中非常灵活。
nextSibling
获取后一个节点,以相对位置查找节点,实际应用中非常灵活。
js组成
JavaScript的组成
- ECMAScript:
- 规定了js基础语法核心知识。
- 比如:变量、分支语句、循环语句、对象等等
- Web APIs :
- DOM 文档对象模型, 定义了一套操作HTML文档的API
- BOM 浏览器对象模型,定义了一套操作浏览器窗口的API
window对象
BOM (Browser Object Model ) 是浏览器对象模型
- window对象是一个全局对象,也可以说是JavaScript中的顶级对象
- 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
- 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
- window对象下的属性和方法调用的时候可以省略window
定时器-延迟函数
JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout
语法:
setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window
间歇函数 setInterval : 每隔一段时间就执行一次, , 平时省略window
清除延时函数:
注意点
- 延时函数需要等待,所以后面的代码先执行
- 返回值是一个正整数,表示定时器的编号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <body> <script>
let timerId = setTimeout(function () { console.log('我只执行一次') }, 3000)
console.log(timerId)
clearTimeout(timerId)
</script> </body>
|
location对象
location (地址) 它拆分并保存了 URL 地址的各个组成部分, 它是一个对象
属性/方法 |
说明 |
href |
属性,获取完整的 URL 地址,赋值时用于地址的跳转 |
search |
属性,获取地址中携带的参数,符号 ?后面部分 |
hash |
属性,获取地址中的啥希值,符号 # 后面部分 |
reload() |
方法,用来刷新当前页面,传入参数 true 时表示强制刷新 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <body> <form> <input type="text" name="search"> <button>搜索</button> </form> <a href="#/music">音乐</a> <a href="#/download">下载</a>
<button class="reload">刷新页面</button> <script> console.log(location.href)
console.log(location.search)
console.log(location.hash)
const btn = document.querySelector('.reload') btn.addEventListener('click', function () { location.reload(true) }) </script> </body>
|
navigator对象
navigator是对象,该对象下记录了浏览器自身的相关信息
常用属性和方法:
1 2 3 4 5 6 7 8 9 10
| (function () { const userAgent = navigator.userAgent const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/) const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/) if (android || iphone) { location.href = 'http://m.itcast.cn' }})();
|
histroy对象
history (历史)是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退等
使用场景
history对象一般在实际开发中比较少用,但是会在一些OA 办公系统中见到。
常见方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <body> <button class="back">←后退</button> <button class="forward">前进→</button> <script>
const forward = document.querySelector('.forward') forward.addEventListener('click', function () { history.go(1) }) const back = document.querySelector('.back') back.addEventListener('click', function () { history.go(-1) }) </script> </body>
|
本地存储(今日重点)
本地存储:将数据存储在本地浏览器中
常见的使用场景:
https://todomvc.com/examples/vanilla-es6/ 页面刷新数据不丢失
好处:
1、页面刷新或者关闭不丢失数据,实现数据持久化
2、容量较大,sessionStorage和 localStorage 约 5M 左右
localStorage(重点)
作用: 数据可以长期保留在本地浏览器中,刷新页面和关闭页面,数据也不会丢失
特性:以键值对的形式存储,并且存储的是字符串, 省略了window
67604963508
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>本地存储-localstorage</title> </head>
<body> <script> localStorage.setItem('age', 18)
console.log(typeof localStorage.getItem('age'))
localStorage.removeItem('age') </script> </body>
</html>
|
sessionStorage(了解)
特性:
- 用法跟localStorage基本相同
- 区别是:当页面浏览器被关闭时,存储在 sessionStorage 的数据会被清除
存储:sessionStorage.setItem(key,value)
获取:sessionStorage.getItem(key)
删除:sessionStorage.removeItem(key)
localStorage 存储复杂数据类型
问题:本地只能存储字符串,无法存储复杂数据类型.
解决:需要将复杂数据类型转换成 JSON字符串,在存储到本地
语法:JSON.stringify(复杂数据类型)
JSON字符串:
- 首先是1个字符串
- 属性名使用双引号引起来,不能单引号
- 属性值如果是字符串型也必须双引号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <script> const goods = { name: '小米', price: 1999 }
localStorage.setItem('goods', JSON.stringify(goods))
</script> </body>
|
问题:因为本地存储里面取出来的是字符串,不是对象,无法直接使用
**解决: **把取出来的字符串转换为对象
语法:JSON.parse(JSON字符串)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <body> <script> const goods = { name: '小米', price: 1999 }
localStorage.setItem('goods', JSON.stringify(goods))
console.log(JSON.parse(localStorage.getItem('goods')))
</script> </body>
|
综合案例
数组map 方法
使用场景:
map 可以遍历数组处理数据,并且返回新的数组
语法:
1 2 3 4 5 6 7 8 9 10 11 12
| <body> <script> const arr = ['red', 'blue', 'pink'] const newArr = arr.map(function (ele, index) { return ele + '颜色' }) console.log(newArr) </script> </body>
|
map 也称为映射。映射是个术语,指两个元素的集之间元素相互“对应”的关系。
map重点在于有返回值,forEach没有返回值(undefined)
数组join方法
作用:join() 方法用于把数组中的所有元素转换一个字符串
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <body> <script> const arr = ['red', 'blue', 'pink']
const newArr = arr.map(function (ele, index) { return ele + '颜色' }) console.log(newArr)
console.log(newArr.join()) console.log(newArr.join('')) console.log(newArr.join('|')) </script> </body>
|