Web API 基本认知
介绍
严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系;
ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的;
浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。


ECMAScript 运行在浏览器中然后再结合 Web APIs 才是真正的 JavaScript,Web APIs 的核心是 DOM 和 BOM。
DOM
概念
DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的API;
简而言之,DOM 是用来动态修改 HTML 的,其目的是开发网页特效及用户交互。
DOM 树
将 HTML 文档以树状结构直观的表现出来,我们称之为 文档树 或 DOM 树;
作用:文档树直观的体现了标签与标签之间的关系
<!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 对象,主要分为元素节点、属性节点、文本节点等。
- 【元素节点】其实就是 HTML 标签,如上图中 
head、div、body等都属于元素节点。 - 【属性节点】是指 HTML 标签中的属性,如上图中 
a标签的href属性、div标签的class属性。 - 【文本节点】是指 HTML 标签的文字内容,如 
title标签中的文字。 - 【根节点】特指 
html标签。 - 其它…
 
小结:
- DOM对象:浏览器根据html标签生成的 JS对象
 - DOM的核心思想:把网页内容当做对象来处理
 - 修改这个对象的属性会自动映射到标签身上
 
document
document 是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法,document 是学习 DOM 的核心。
获取DOM对象
根据CSS选择器来获取DOM元素(重点)
querySelector('css选择器')选择匹配的第一个元素querySelectorAll('css选择器')满足条件的元素集合 返回NodeList对象集合(伪数组)
注意:
- 小括号里面的参数必须是字符串,也就是必须加引号
 
其他获取DOM元素方法(了解)
getElementByIdgetElementsByTagName
总结:
- document.getElementById 专门获取元素类型节点,根据标签的 
id属性查找 - 任意 DOM 对象都包含 nodeType 属性,用来检检测节点类型
 
操作元素内容
通过修改 DOM 的文本内容,动态改变网页的内容。
innerText将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析。
<script>
  // innerText 将文本内容添加/更新到任意标签位置
  const intro = document.querySelector('.intro')
  // intro.innerText = '嗨~ 我叫李雷!'
  // intro.innerText = '<h4>嗨~ 我叫李雷!</h4>'
</script>
 
innerHTML将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。
<script>
  // innerHTML 将文本内容添加/更新到任意标签位置
  const intro = document.querySelector('.intro')
  intro.innerHTML = '嗨~ 我叫韩梅梅!'
  intro.innerHTML = '<h4>嗨~ 我叫韩梅梅!</h4>'
</script>
 
总结:如果文本内容中包含 html 标签时推荐使用 innerHTML,否则建议使用 innerText 属性。
操作元素属性
常用属性修改
直接能过属性名修改,最简洁的语法。
语法:对象.属性 = 值
<script>
  // 1. 获取 img 对应的 DOM 元素
  const pic = document.querySelector('.pic')
    // 2. 修改属性
  pic.src = './images/lion.webp'
  pic.width = 400;
  pic.alt = '图片不见了...'
</script>
 
控制样式属性
- 通过修改行内样式 
**style**属性,实现对样式的动态修改。 
语法:对象.style.样式属性 = 值
<body>
  <div class="box">随便一些文本内容</div>
  <script>
    // 获取 DOM 节点
    const box = document.querySelector('.intro')
    
    box.style.color = 'red'
    box.style.width = '300px'
    // css 属性的 - 连接符与 JavaScript 的 减运算符
    // 冲突,所以要改成驼峰法
    box.style.backgroundColor = 'pink'
  </script>
</body>
 
任何标签都有 style 属性,通过 style 属性可以动态更改网页标签的样式;
如要遇到 css 属性中包含字符 - 时,要将 - 去掉并将其后面的字母改成大写,如 background-color 要写成 box.style.backgroundColor
- 操作类名(className) 操作CSS
 
如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。
语法:元素.className = 'css类名'
<head>
  <style>
      .pink {
          background: pink;
          color: hotpink;
      }
  </style>
</head>
<body>
  <div class="box">随便一些文本内容</div>
  <script>
    // 获取 DOM 节点
    const box = document.querySelector('.intro')
    
    box.className = 'pink'
  </script>
</body>
 
注意:
- 由于class是关键字, 所以使用className去代替
 - className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名
 
- 通过 classList 操作类控制CSS
 
为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名。
  <script>
      // 获取元素
      // let box = document.querySelector('css选择器')
      let box = document.querySelector('div')
    
      // 1. add() 追加一个类
       box.classList.add('active')
      // 2. remove() 移除一个类
       box.classList.remove('active')
      // 3. toggle() 切换一个类 (有就删掉,没有就加上)
       box.classList.toggle('active')
      // 4. contains() 判断是否包含某个类,如果有则返回true,么有则返回false
       box.classList.contains('active')
  </script>
 
操作表单元素属性
表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框
正常的有属性有取值的跟其他的标签属性没有任何区别
获取:DOM对象.属性名
设置:DOM对象.属性名= 新值
自定义属性
标准属性: 标签天生自带的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、selected
自定义属性:
在html5中推出来了专门的data-自定义属性
在标签上一律以data-开头
在DOM对象上一律以dataset对象方式获取
<body>
   <div data-id="1"> 自定义属性 </div>
    <script>
        // 1. 获取元素
        let div = document.querySelector('div')
        // 2. 获取自定义属性值
         console.log(div.dataset.id)
    </script>
</body>
 
间歇函数
setInterval 是 JavaScript 中内置的函数,它的作用是间隔固定的时间自动重复执行另一个函数,也叫定时器函数。
<script>
  // 1. 定义一个普通函数
  function repeat() {
    console.log('不知疲倦的执行下去....')
  }
  // 2. 使用 setInterval 调用 repeat 函数
  // 间隔 1000 毫秒,重复调用 repeat
  setInterval(repeat, 1000)
  //关闭定时器
  let 变量名 = setInterval(函数,间隔时间)
  clearInterval(变量名)
</script>
 
注意:
- 函数名字不需要加括号
 - 定时器返回的是一个id数字
 
事件
事件是在编程时系统内发生的动作或者发生的事情,比如用户在网页上单击一个按钮
事件监听
就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为绑定事件或者注册事件。
比如,鼠标经过显示下拉菜单,比如点击可以播放轮播图等等。
语法:
元素对象.addEventListener('事件类型',要执行的函数)
 
<body>
  <button>点击</button>
  <script>
    // 需求: 点击了按钮,弹出一个对话框
    // 1. 事件源  按钮  
    // 2.事件类型 点击鼠标  click 字符串
    // 3. 事件处理程序  弹出对话框
    const btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      alert('你早呀~')
    })
  </script>
</body>
 
事件监听三要素:
- 事件源:那个dom元素被事件触发了,要获取dom元素
 - 事件类型:用什么方式触发,比如鼠标单击click、鼠标经过mouseover等
 - 事件调用的函数:要做什么事
 
完成事件监听分成3个步骤:
- 获取 DOM 元素
 - 通过 
addEventListener方法为 DOM 节点添加事件监听 - 等待事件触发,如用户点击了某个按钮时便会触发 
click事件类型 - 事件触发后,相对应的回调函数会被执行
 
事件类型
【事件类型】决定了事件被触发的方式,如 <font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">click</font> 代表鼠标单击,<font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">dblclick</font> 代表鼠标双击。
JS中所有的事件类型:HTML DOM 事件 (w3school.com.cn)
事件处理程序
addEventListener 的第2个参数是函数,这个函数会在事件被触发时立即被调用,在这个函数中可以编写任意逻辑的代码,如改变 DOM 文本颜色、文本内容等。
【事件处理程序】决定了事件触发后应该执行的逻辑。
事件类型
将众多的事件类型分类可分为:鼠标事件、键盘事件、表单事件、焦点事件等。
鼠标事件
鼠标事件是指跟鼠标操作相关的事件,如单击、双击、移动等。
<font style="color:rgb(51, 51, 51);">slider</font>监听鼠标是否移入 DOM 元素<font style="color:rgb(51, 51, 51);">mouseleave</font>监听鼠标是否移出 DOM 元素
键盘事件
keydown键盘按下触发keyup键盘抬起触发
焦点事件
focus获得焦点blur失去焦点
文本框输入事件
input
事件对象
任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。
<body>
  <h3>事件对象</h3>
  <p>任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。</p>
  <hr>
  <div class="box"></div>
  <script>
    // 获取 .box 元素
    const box = document.querySelector('.box')
    // 添加事件监听
    box.addEventListener('click', function (e) {
      console.log('任意事件类型被触发后,相关信息会以对象形式被记录下来...');
      // 事件回调函数的第1个参数即所谓的事件对象
      console.log(e)
    })
  </script>
</body>
 
事件回调函数的【第1个参数】即所谓的事件对象,通常习惯性的将这个对数命名为 <font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">event</font>、<font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">ev</font> 、e。
接下来简单看一下事件对象中包含了哪些有用的信息:
<font style="color:rgb(51, 51, 51);">ev.type</font>当前事件的类型<font style="color:rgb(51, 51, 51);">ev.clientX/Y</font>光标相对浏览器窗口的位置<font style="color:rgb(51, 51, 51);">ev.offsetX/Y</font>光标相于当前 DOM 元素的位置key用户按下的键盘键的值
注:在事件回调函数内部通过 window.event 同样可以获取事件对象。
环境对象
能够分析判断函数运行在不同环境中 this 所指代的对象。
环境对象指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境。
<script>
  // 声明函数
  function sayHi() {
    // this 是一个变量
    console.log(this);
  }
  // 声明一个对象
  let user = {
    name: '张三',
    sayHi: sayHi // 此处把 sayHi 函数,赋值给 sayHi 属性
  }
  
  let person = {
    name: '李四',
    sayHi: sayHi
  }
  // 直接调用
  sayHi() // window
  window.sayHi() // window
  // 做为对象方法调用
  user.sayHi()// user
	person.sayHi()// person
</script>
 
结论:
this本质上是一个变量,数据类型为对象- 函数的调用方式不同 
this变量的值也不同 - 【谁调用 
this就是谁】是判断this值的粗略规则 - 函数直接调用时实际上 
window.sayHi()所以this的值为window 
回调函数
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数。
<script>
  // 声明 foo 函数
  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 就是所谓的回调函数。
结论:
- 回调函数本质还是函数,只不过把它当成参数使用
 - 使用匿名函数做为回调函数比较常见
 
事件流
事件流指的是事件完整执行过程中的流动路径,是对事件执行过程的描述。

如上图所示,任意事件被触发时总会经历两个阶段:【捕获阶段】和【冒泡阶段】。
- 捕获阶段是【从父到子】的传导过程,冒泡阶段是【从子向父】的传导过程。
 
捕获和冒泡
事件捕获概念:
- 从DOM的根元素开始去执行对应的事件 (从外到里)
 
事件冒泡概念:
- 当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件
 
addEventListener第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发addEventListener第3个参数为true表示捕获阶段触发,false表示冒泡阶段触发,默认值为false- 事件流只会在父子元素具有相同事件类型时才会产生影响
 - 绝大部分场景都采用默认的冒泡模式(其中一个原因是早期 IE 不支持捕获)
 
<body>
  <h3>事件流</h3>
  <p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
  <div class="outer">
    <div class="inner"></div>
  </div>
  <script>
    // 获取嵌套的3个节点
    const outer = document.querySelector('.outer')
    const inner = document.querySelector('.inner')
    // 外层的盒子
    outer.addEventListener('click', function () {
      console.log('outer...')
    }, truez) // true 表示在捕获阶段执行事件
    
    // 中间的盒子
    outer.addEventListener('click', function () {
      console.log('inner...')
    }, true)
  </script>
</body>
 
阻止冒泡
阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。
**前提:**阻止事件冒泡需要拿到事件对象
语法:
- 阻止冒泡:
事件对象.stopPropagation() - 组织元素默认行为:
e.preventDefault() 
<body>
  <h3>阻止冒泡</h3>
  <p>阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。</p>
  <div class="outer">
    <div class="inner">
      <div class="child"></div>
    </div>
  </div>
  <script>
    // 获取嵌套的3个节点
    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 没有冒泡效果 (推荐)
解绑事件
addEventListener方式,必须使用;removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])
function fun() {
  alert('点击了')
}
//绑定事件
bth.addEventListener('click', fun)
//解绑事件
bth.removeEventListener('click', fun)
 
注意:匿名函数无法被解绑。
事件委托
事件委托是利用事件流的特征解决一些现实开发需求的知识技巧,主要的作用是提升程序效率。
- 优点:减少注册次数,可以提高程序性能;
 - 原理:利用事件冒泡的特点。 
  
- 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
 
 - 实现:事件对象.target. tagName 可以获得真正触发事件的元素
 
大量的事件监听是比较耗费性能的,如下代码所示
<script>
  // 假设页面中有 10000 个 button 元素
  const buttons = document.querySelectorAll('table button');
  for(let i = 0; i <= buttons.length; i++) {
    // 为 10000 个 button 元素添加了事件
    buttons.addEventListener('click', function () {
      // 省略具体执行逻辑...
    })
  }
</script>
 
利用事件流的特征,可以对上述的代码进行优化,事件的的冒泡模式总是会将事件流向其父元素的,如果父元素监听了相同的事件类型,那么父元素的事件就会被触发并执行,正是利用这一特征对上述代码进行优化,如下代码所示:
<script>
  // 假设页面中有 10000 个 button 元素
  let buttons = document.querySelectorAll('table button');
  
  // 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
  let parents = document.querySelector('table');
  parents.addEventListener('click', function () {
    console.log('点击任意子元素都会触发事件...');
  })
</script>
 
我们的最终目的是保证只有点击 button 子元素才去执行事件的回调函数,如何判断用户点击是哪一个子元素呢?
- 事件对象中的属性 
<font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">target</font>或<font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">srcElement</font>属性表示真正触发事件的元素,它是一个元素类型的节点。 
<script>
  // 假设页面中有 10000 个 button 元素
  const buttons = document.querySelectorAll('table button')
  
  // 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
  const parents = document.querySelector('table')
  parents.addEventListener('click', function (ev) {
    // console.log(ev.target);
    // 只有 button 元素才会真正去执行逻辑
    if(ev.target.tagName === 'BUTTON') {
      // 执行的逻辑
    }
  })
</script>
 
优化过的代码只对祖先元素添加事件监听,相比对 10000 个元素添加事件监听执行效率要高许多!!!
其他事件
页面加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
有些时候需要等页面资源全部处理完了做一些事情
事件名:load
监听页面所有资源加载完毕:
window.addEventListener('load', function() {
    // xxxxx
})
 
事件名:DOMContentLoaded
监听页面DOM加载完毕,而无需等待样式表、图像等完全加载。
window.addEventListener('DOMContentLoaded', function() {
    // xxxxx
})
 
元素滚动事件
滚动条在滚动的时候持续触发的事件
window.addEventListener('scroll', function() {
    // xxxxx
})
 
scrollLeft和scrollTop (属性)
- 获取被卷去的大小
 - 获取元素内容往左、往上滚出去看不到的距离
 - 这两个值是可读写的
 
」//点击返回页面顶部
const backTop document.querySelector('#backTop')
backTop.addEventListener('click',function (){
//可读写
//document.documentElement.scrollTop	//页面滚动top值
//window.scrollTo(x,y)
window,scrollTo(0,0)
}
 

页面尺寸事件
会在窗口尺寸改变的时候触发事件:
window.addEventListener('resize', function() {
    // xxxxx
})
 
元素尺寸与位置
获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetWidth和offsetHeight
获取出来的是数值,方便计算
注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
获取位置:
element.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置
总结:
| 属性 | 作用 | 说明 | 
|---|---|---|
scrollLeft 和 scrollTop | 被卷去的头部和左侧 | 配合页面滚动来用,可读写 | 
clientWidth 和 clientHeight | 获得元素宽度和高度 | 不包含border,margin,滚动条用于js获取元素大小,只读属性 | 
offsetWidth 和 offsetHeight | 获得元素宽度和高度 | 包含border、padding,滚动条等,只读 | 
offsetLeft和 offsetTop | 获取元素距离自己定位父级元素的左、上距离 | 获取元素位置的时候使用,只读属性 | 
日期对象
日期对象:用来表示时间的对象
作用:可以得到当前系统时间
实例化
在代码中发现了new关键字时,一般将这个操作称为实例化。
- 获取当前时间
 
const date = new Date()
 
- 获取指定时间
 
const date = new Date('2008-8-8')
console.log(date)
 
方法
使用场景:因为日期对象返回的数据我们不能直接使用,所以需要转换为实际开发中常用的格式
  // 1. 实例化
 const date = new Date();
 // 2. 调用时间对象方法
 // 通过方法分别获取年、月、日,时、分、秒
 const year = date.getFullYear(); // 四位年份
 const month = date.getMonth(); // 0 ~ 11
 
| 方法 | 作用 | 说明 | 
|---|---|---|
| getFullYear() | 获得年份 | 获取四位年份 | 
| getMonth() | 获得月份 | 取值为0~11 | 
| getDate() | 获取月份中的每一天 | 不同月份取值也不相同 | 
| getDay() | 获取星期 | 取值为0~6,0通常表示星期日 | 
| getHours() | 获取小时 | 取值为0~23 | 
| getMinutes() | 获取分钟 | 取值为0~59 | 
| getSeconds() | 获取秒 | 取值为0~59 | 
时间戳
时间戳是指1970年01月01日00时00分00秒起至现在的总秒数或毫秒数,它是一种特殊的计量时间的方式。
算法:
- 将来的时间戳·现在的时间戳=剩余时间毫秒数
 - 剩余时间毫秒数转换为剩余时间的年月日时分秒就是倒计时时间
 
注:ECMAScript 中时间戳是以毫秒计的。
三种方式获取时间戳:
- 使用 
getTime()方法 
const date = new Date()
console.log(date.getTime())
 
- 简写 
+new Date() 
console.log(+new Date())
 
- 使用
Date.now() 
- 无需实例化
 - 但是只能得到当前的时间戳,而前面两种可以返回指定时间的时间戳
 
console.log(Date.now())
 
注:重点记住 +new Date() 因为可以返回当前时间戳或者指定的时间戳
DOM 节点
增加节点
在已有的 DOM 节点中插入新的 DOM 节点时,需要关注两个关键因素:首先要得到新的 DOM 节点,其次在哪个位置插入这个节点
- 创建节点:
 
//创造一个新的元素节点
document.createElement('标签名')
 
- 追加节点:
 
//插入到这个父元素的最后
父元素.appendChild(要插入的元素)
//插入到某个子元素的前面
父元素.insertBefore(要插入的元素,在哪个元素前面)
 
- 克隆节点
 
//克隆一个己有的元素节点
元素.cloneNode(布尔值)
 
cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值
- 若为true,则代表克隆时会包含后代节点一起克隆
 - 若为false,则代表克隆时不包含后代节点,默认为false
 
结论:
createElement动态创建任意 DOM 节点cloneNode复制现有的 DOM 节点,传入参数 true 会复制所有子节点insertBefore在父节点中任意子节点之前插入新节点appendChild在末尾(结束标签前)插入节点
删除节点
删除现有的 DOM 节点,也需要关注两个因素:首先由父节点删除子节点,其次是要删除哪个子节点。
语法:
父元素.removeChild(要删除的元素)
 
结论:
removeChild删除节点时一定是由父子关系,如不存在父子关系则删除不成功。- 删除节点和隐藏节点(display:none)有区别的:隐藏节点还是存在的,但是删除,则从html中删除节点
 
查找节点
DOM 树中的任意节点都不是孤立存在的,它们要么是父子关系,要么是兄弟关系,不仅如此,我们可以依据节点之间的关系查找节点。
父子关系
<body>
  <button class="btn1">所有的子节点</button>
  <!-- 获取 ul 的子节点 -->
  <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只获取元素类型节点(重点)
<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>
    // 获取所有 button 节点,并添加事件监听
    const buttons = document.querySelectorAll('table button')
    for(let i = 0; i < buttons.length; i++) {
      buttons[i].addEventListener('click', function () {
        // console.log(this.parentNode); // 父节点 td
        // console.log(this.parentNode.parentNode); // 爷爷节点 tr
        this.parentNode.parentNode.style.color = 'red'
      })
    }
  </script>
</body>
 
结论:parentNode 获取父节点,以相对位置查找节点,找不到返回为null,实际应用中非常灵活。
兄弟关系
<body>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JavaScript 基础</li>
    <li>Web APIs</li>
  </ul>
  <script>
    // 获取所有 li 节点
    const lis = document.querySelectorAll('ul li')
    // 对所有的 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获取后一个节点,以相对位置查找节点,实际应用中非常灵活。
M端事件
触屏事件 touch(也称触摸事件),Android 和 IOS 都有。
常见的触屏事件如下:
当然,以下是您提供的表格内容:
| 触屏touch事件 | 说明 | 
|---|---|
| touchstart | 手指触摸到一个DOM元素时触发 | 
| touchmove | 手指在一个DOM元素上滑动时触发 | 
| touchend | 手指从一个DOM元素上移开时触发 | 
重绘和回流
浏览器是如何进行界面渲染的:

-  
解析(Parser)HTML,生成DOM树(DOM Tree)
 -  
同时解析(Parser) CSS,生成样式规则 (Style Rules)
 -  
根据DOM树和样式规则,生成渲染树(Render Tree)
 -  
进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小)
 -  
进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制
 -  
Display: 展示在页面上
 -  
回流(重排)
 
当 Render Tree 中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为 回流。
- 重绘
 
由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、outline等), 称为重绘。
重绘不一定引起回流,而回流一定会引起重绘。
会导致回流(重排)的操作:
- 页面的首次刷新
 - 浏览器的窗口大小发生改变
 - 元素的大小或位置发生改变
 - 改变字体的大小
 - 内容的变化(如:input框的输入,图片的大小)
 - 激活css伪类 (如::hover)
 - 脚本操作DOM(添加或者删除可见的DOM元素)
 
简单理解影响到布局了,就会有回流
BOM
Window对象
BOM (Browser Object Model ) 是浏览器对象模型
- window对象是一个全局对象,也可以说是JavaScript中的顶级对象
 - 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
 - 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
 - window对象下的属性和方法调用的时候可以省略window
 

定时器-延迟函数
JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout
语法:
setTimeout(回调函数, 延迟时间)
 
- setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window
 - 间歇函数 setInterval : 每隔一段时间就执行一次, , 平时省略window
 
清除延时函数:
clearTimeout(timerId)
 
注意点
- 延时函数需要等待,所以后面的代码先执行
 - 返回值是一个正整数,表示定时器的编号
 
<body>
  <script>
    // 定时器之延迟函数
    // 1. 开启延迟函数
    let timerId = setTimeout(function () {
      console.log('我只执行一次')
    }, 3000)
    // 1.1 延迟函数返回的还是一个正整数数字,表示延迟函数的编号
    console.log(timerId)
    // 1.2 延迟函数需要等待时间,所以下面的代码优先执行
    // 2. 关闭延迟函数
    clearTimeout(timerId)
  </script>
</body>
 
location对象
location (地址) 它拆分并保存了 URL 地址的各个组成部分, 它是一个对象
| 属性/方法 | 说明 | 
|---|---|
| href | 属性,获取完整的 URL 地址,赋值时用于地址的跳转 | 
| search | 属性,获取地址中携带的参数,符号 ?后面部分 | 
| hash | 属性,获取地址中的啥希值,符号 # 后面部分 | 
| reload() | 方法,用来刷新当前页面,传入参数 true 时表示强制刷新 | 
<body>
  <form>
    <input type="text" name="search"> <button>搜索</button>
  </form>
  <a href="#/music">音乐</a>
  <a href="#/download">下载</a>
  <button class="reload">刷新页面</button>
  <script>
    // location 对象  
    // 1. href属性 (重点) 得到完整地址,赋值则是跳转到新地址
    console.log(location.href)
    // location.href = 'http://www.itcast.cn'
    // 2. search属性  得到 ? 后面的地址 
    console.log(location.search)  // ?search=笔记本
    // 3. hash属性  得到 # 后面的地址
    console.log(location.hash)
    // 4. reload 方法  刷新页面
    const btn = document.querySelector('.reload')
    btn.addEventListener('click', function () {
      // location.reload() // 页面刷新
      location.reload(true) // 强制页面刷新 ctrl+f5
    })
  </script>
</body>
 
navigator对象
navigator是对象,该对象下记录了浏览器自身的相关信息
常用属性和方法:
- 通过 userAgent 检测浏览器的版本及平台
 
// 检测 userAgent(浏览器信息)
(function () {
  const userAgent = navigator.userAgent
  // 验证是否为Android或iPhone
  const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
  const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
  // 如果是Android或iPhone,则跳转至移动站点
  if (android || iphone) {
    location.href = 'http://m.itcast.cn'
  }})();
 
histroy对象
history (历史)是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退等。
使用场景:
history对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。
常见方法:

JS 执行机制
JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。
- 同步
 
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同
步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。
- 异步
 
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。
他们的本质区别: 这条流水线上各个流程的执行顺序不同。
- 同步任务
 
同步任务都在主线程上执行,形成一个执行栈。
- 异步任务
 
JS 的异步是通过回调函数实现的。
一般而言,异步任务有以下三种类型:
- 普通事件,如 click、resize 等
 - 资源加载,如 load、error 等
 - 定时器,包括 setInterval、setTimeout 等
 
异步任务相关添加到任务队列中(任务队列也称为消息队列)。

- 先执行执行栈中的同步任务。
 - 异步任务放入任务队列中。
 - 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
 

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

本地存储
本地存储:将数据存储在本地浏览器中。
常见的使用场景:
页面刷新数据不丢失。
好处:
1、页面刷新或者关闭不丢失数据,实现数据持久化
2、容量较大,sessionStorage和 localStorage 约 5M 左右
localStorage(重点)
**作用:**数据可以长期保留在本地浏览器中,刷新页面和关闭页面,数据也不会丢失
特性:
- 以键值对的形式存储,并且存储的是字符串, 省略了window
 - 可以多窗口(页面)共享(同一浏览器可以共享)
 
- 存储数据
 
localStorage.setItem(key,value)
 
- 读取数据
 
localStorage.getItem(key)
 
- 删除数据
 
localStorage.removeItem(key)
 
sessionStorage(了解)
特性:
- 用法跟 localStorage 基本相同
 - 生命周期为关闭浏览器窗口
 - 在同一个窗口(页面)下数据可以共享
 - 以键值对的形式存储使用
 
区别是:当页面浏览器被关闭时,存储在 sessionStorage 的数据会被清除
- 存储
 
sessionStorage.setItem(key,value)
 
- 获取
 
sessionStorage.getItem(key)
 
- 删除
 
sessionStorage.removeItem(key)
 
localStorage 存储复杂数据类型
**问题:**本地只能存储字符串,无法存储复杂数据类型.
**解决:**需要将复杂数据类型转换成 JSON字符串,在存储到本地
语法:JSON.stringify(复杂数据类型)
JSON字符串:
- 首先是1个字符串
 - 属性名使用双引号引起来,不能单引号
 - 属性值如果是字符串型也必须双引号
 
<body>
  <script>
    // 本地存储复杂数据类型
    const goods = {
      name: '小米',
      price: 1999
    }
    // localStorage.setItem('goods', goods)
    // console.log(localStorage.getItem('goods'))
    // 1. 把对象转换为JSON字符串  JSON.stringify
    localStorage.setItem('goods', JSON.stringify(goods))
    // console.log(typeof localStorage.getItem('goods'))
  </script>
</body>
 
**问题:**因为本地存储里面取出来的是字符串,不是对象,无法直接使用
**解决:**把取出来的字符串转换为对象
语法:JSON.parse(JSON字符串)
<body>
  <script>
    // 本地存储复杂数据类型
    const goods = {
      name: '小米',
      price: 1999
    }
    // localStorage.setItem('goods', goods)
    // console.log(localStorage.getItem('goods'))
    // 1. 把对象转换为JSON字符串  JSON.stringify
    localStorage.setItem('goods', JSON.stringify(goods))
    // console.log(typeof localStorage.getItem('goods'))
    // 2. 把JSON字符串转换为对象  JSON.parse
    console.log(JSON.parse(localStorage.getItem('goods')))
  </script>
</body>
 
数组 map 方法
使用场景:
map 可以遍历数组处理数据,并且返回新的数组
语法:
<body>
  <script>
  const arr = ['red', 'blue', 'pink']
  // 1. 数组 map方法 处理数据并且 返回一个数组
   const newArr = arr.map(function (ele, index) {
    // console.log(ele)  // 数组元素
    // console.log(index) // 索引号
    return ele + '颜色'
    })
console.log(newArr)
</script>
</body>
 
map 也称为映射。映射是个术语,指两个元素的集之间元素相互“对应”的关系。
map重点在于有返回值,forEach没有返回值(undefined)
数组 join 方法
作用:join() 方法用于把数组中的所有元素转换一个字符串
语法:
<body>
  <script>
    const arr = ['red', 'blue', 'pink']
    // 2. 数组join方法  把数组转换为字符串
    // 小括号为空则逗号分割
    console.log(arr.join())  // red颜色,blue颜色,pink颜色
    // 小括号是空字符串,则元素之间没有分隔符
    console.log(arr.join(''))  //red颜色blue颜色pink颜色
    console.log(arr.join('|'))  //red颜色|blue颜色|pink颜色
  </script>
</body>
 
正则表达式
正则表达式(Regular Expression)是一种字符串匹配的模式(规则)
使用场景:
- 例如验证表单:手机号表单要求用户只能输入11位的数字 (匹配)
 - 过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等
 
参考文档:
- MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
 - 正则测试工具:在线正则表达式测试 (oschina.net)
 
正则基本使用
- 定义规则:
 
const 变量名 =  /表达式/
 
- 其中` /   / `是正则表达式字面量
- 正则表达式也是`对象 `
 
- 判断是否有符合规则的字符串: 
  
test()方法用来查看正则表达式与指定的字符串是否匹配- 如果正则表达式与指定的字符串匹配 ,返回
true,否则false 
 
<body>
  <script>
    // 正则表达式的基本使用
    const str = 'web前端开发'
    // 1. 定义规则
    const reg = /web/
    // 2. 使用正则  test()
    console.log(reg.test(str))  // true  如果符合规则匹配上则返回true
    console.log(reg.test('java开发'))  // false  如果不符合规则匹配上则返回 false
  </script>
</body>
 
- 检索(查找)符合规则的字符串: 
  
exec()方法在一个指定字符串中执行一个搜索匹配
 
 // 要检测的字符串
const str='IT培训,前端开发培训,IT培训课程,web前端培训,Java培训,人工智能培训'
 //1. 定义正则表达式,检测规则
const reg = /前端/
  
 //2. 检测方法
console.log(reg.exec(str))	//如果匹配成功,返回一个数组,否则返回null
 

元字符
- 普通字符:
 
- 大多数的字符仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。
 - 普通字符只能够匹配字符串中与它们相同的字符。
 - 比如,规定用户只能输入英文26个英文字母,普通字符的话 
/[abcdefghijklmnopqrstuvwxyz]/ 
- 元字符(特殊字符)
 
- 是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能。
 - 比如,规定用户只能输入英文26个英文字母,换成元字符写法: 
/[a-z]/ 
边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置(表示位置,开头和结尾,必须用什么开头,用什么结尾),主要有两个字符:
| 符号 | 说明 | 
|---|---|
^ | 表示匹配行首的文本(以谁开始) | 
$ | 表示匹配行尾的文本(以谁结束) | 
如果 ^ 和 $ 在一起,表示必须是精确匹配。
<body>
  <script>
    // 元字符之边界符
    // 1. 匹配开头的位置 ^
    const reg = /^web/
    console.log(reg.test('web前端'))  // true
    console.log(reg.test('前端web'))  // false
    console.log(reg.test('前端web学习'))  // false
    console.log(reg.test('we'))  // false
    // 2. 匹配结束的位置 $
    const reg1 = /web$/
    console.log(reg1.test('web前端'))  //  false
    console.log(reg1.test('前端web'))  // true
    console.log(reg1.test('前端web学习'))  // false
    console.log(reg1.test('we'))  // false  
    // 3. 精确匹配 ^ $
    const reg2 = /^web$/
    console.log(reg2.test('web前端'))  //  false
    console.log(reg2.test('前端web'))  // false
    console.log(reg2.test('前端web学习'))  // false
    console.log(reg2.test('we'))  // false 
    console.log(reg2.test('web'))  // true
    console.log(reg2.test('webweb'))  // flase 
  </script>
</body>
 
量词
量词用来设定某个模式重复次数。
| 量词 | 说明 | 
|---|---|
* | 重复次数 >= 0 次 | 
+ | 重复次数 >= 1 次 | 
? | 重复次数 0 | 
{n} | 重复n次 | 
{n,} | 重复次数 >= n | 
{n,m} | n =< 重复次数 <= m | 
注意: 逗号左右两侧千万不要出现空格。
<body>
  <script>
    // 元字符之量词
    // 1. * 重复次数 >= 0 次
    const reg1 = /^w*$/
    console.log(reg1.test(''))  // true
    console.log(reg1.test('w'))  // true
    console.log(reg1.test('ww'))  // true
    console.log('-----------------------')
    // 2. + 重复次数 >= 1 次
    const reg2 = /^w+$/
    console.log(reg2.test(''))  // false
    console.log(reg2.test('w'))  // true
    console.log(reg2.test('ww'))  // true
    console.log('-----------------------')
    // 3. ? 重复次数  0 || 1 
    const reg3 = /^w?$/
    console.log(reg3.test(''))  // true
    console.log(reg3.test('w'))  // true
    console.log(reg3.test('ww'))  // false
    console.log('-----------------------')
    // 4. {n} 重复 n 次
    const reg4 = /^w{3}$/
    console.log(reg4.test(''))  // false
    console.log(reg4.test('w'))  // flase
    console.log(reg4.test('ww'))  // false
    console.log(reg4.test('www'))  // true
    console.log(reg4.test('wwww'))  // false
    console.log('-----------------------')
    // 5. {n,} 重复次数 >= n 
    const reg5 = /^w{2,}$/
    console.log(reg5.test(''))  // false
    console.log(reg5.test('w'))  // false
    console.log(reg5.test('ww'))  // true
    console.log(reg5.test('www'))  // true
    console.log('-----------------------')
    // 6. {n,m}   n =< 重复次数 <= m
    const reg6 = /^w{2,4}$/
    console.log(reg6.test('w'))  // false
    console.log(reg6.test('ww'))  // true
    console.log(reg6.test('www'))  // true
    console.log(reg6.test('wwww'))  // true
    console.log(reg6.test('wwwww'))  // false
    // 7. 注意事项: 逗号两侧千万不要加空格否则会匹配失败
  </script>
 
范围
表示字符的范围,定义的规则限定在某个范围,比如只能是英文字母,或者数字等等,用表示范围
| 表达式 | 说明 | 
|---|---|
[abc] | 匹配字符集合包含的单个字符。也就是只有a、b、c这三个单字符返回true,可以理解为多选1 | 
[a-z] | 连字符。用来指定字符范围。[a-z]表示**a**到**z**的26个英文字母 | 
[^abc] | 取反符。[^a-z]匹配除了小写字母以外的字符(必须写在中括号里面) | 
. | 匹配除换行符之外的任何单个字符 | 
<body>
  <script>
    // 元字符之范围  []  
    // 1. [abc] 匹配包含的单个字符, 多选1
    const reg1 = /^[abc]$/
    console.log(reg1.test('a'))  // true
    console.log(reg1.test('b'))  // true
    console.log(reg1.test('c'))  // true
    console.log(reg1.test('d'))  // false
    console.log(reg1.test('ab'))  // false
    // 2. [a-z] 连字符 单个
    const reg2 = /^[a-z]$/
    console.log(reg2.test('a'))  // true
    console.log(reg2.test('p'))  // true
    console.log(reg2.test('0'))  // false
    console.log(reg2.test('A'))  // false
    // 想要包含小写字母,大写字母 ,数字
    const reg3 = /^[a-zA-Z0-9]$/
    console.log(reg3.test('B'))  // true
    console.log(reg3.test('b'))  // true
    console.log(reg3.test(9))  // true
    console.log(reg3.test(','))  // flase
    // 用户名可以输入英文字母,数字,可以加下划线,要求 6~16位
    const reg4 = /^[a-zA-Z0-9_]{6,16}$/
    console.log(reg4.test('abcd1'))  // false 
    console.log(reg4.test('abcd12'))  // true
    console.log(reg4.test('ABcd12'))  // true
    console.log(reg4.test('ABcd12_'))  // true
    // 3. [^a-z] 取反符
    const reg5 = /^[^a-z]$/
    console.log(reg5.test('a'))  // false 
    console.log(reg5.test('A'))  // true
    console.log(reg5.test(8))  // true
  </script>
</body>
 
字符类
预定义:某些常见模式的简写方式,区分字母和数字。
| 字符类 | 说明 | 
|---|---|
\d | 匹配0-9之间的任一数字,相当于[0-9] | 
\D | 匹配所有0-9以外的字符,相当于[^0-9] | 
\w | 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_] | 
\W | 除所有字母、数字和下划线以外的字符,相当于[^\w] 或 [^A-Za-z0-9_] | 
\s | 匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f] | 
\S | 匹配非空格的字符,相当于[^\s] 或 [^\t\r\n\v\f] | 
- 日期格式:
/^\d{4}-\d{1,2}-\d{1,2}$/ 
替换和修饰符
replace 替换方法,可以完成字符的替换。
- 语法:
字符串.replace(/正则表达式/,'替换的文本') 
修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等
- 语法:
/表达式/修饰符i是单词 ignore 的缩写,正则匹配时字母不区分大小写g是单词 global 的缩写,匹配所有满足正则表达式的结果
 
<body>
  <script>
    // 替换和修饰符
    const str = '欢迎大家学习前端,相信大家一定能学好前端,都成为前端大神'
    // 1. 替换  replace  需求:把前端替换为 web
    // 1.1 replace 返回值是替换完毕的字符串
    // const strEnd = str.replace(/前端/, 'web') 只能替换一个
    // 2. 修饰符 g 全部替换
    const strEnd = str.replace(/前端/g, 'web')
    console.log(strEnd) 
  </script>
</body>
 
正则插件

change 事件
给 input 注册 change 事件,值被修改并且失去焦点后触发。

















