webAPI-DOM-节点操作
回顾
- 函数表达式:
let fn = function(){} 和传统函数声明调用都是一样fn()【先声明,再调用】 - 自调用函数 :
 
(function(){// 为了防止变量污染;(函数内部变量,外面是用不了!)})();
回调函数:
- 当一个函数(回调函数)被作为参数传入另外一个函数的时候。
 - 回:回头才调用,未来才会执行;dom.addEventListener(“click”,回调函数)
 
事件级别:
发展历程
- L0 :dom.on事件类型= 事件执行函数;
 - L1:推出个标准,没有具体代码模型;
 - L2:
dom.addEventListener("事件类型",回调函数,false) - L3:添加新的事件类型、鼠标事件、键盘事件、表达
 
L0 L2:
- 注册:L0多次注册同样事件类型,被覆盖!L2不会被覆盖
 - 移除:确认不需要dom具有某个事件,L0:dom.on事件类型 = null;  L2:
dom.removeEventListener("事件类型",函数名) 
L3鼠标事件:
- mouseover:移入
 - mouseout:移出
 mousemove:移动;给谁注册?在页面或者文档上移动:document;
- 事件对象:把用户的一次行为看做一个对象;对象获取鼠标位置
 语法:
- 1.形参e;
 - 2.坐标:
e.pageX 页面左上角 e.clientX 窗口左上角 e.offsetX 当前dom节点左上角 
- mousedown:落下
 - mouseup:弹起;
 
目录
- 递归函数
 - 节点关系与查找
 - 节点操作
 - 事件流
 
递归函数
目标:知道递归函数的本质及特征
**递归**是函数的高级用法,本质上是函数自己调用自己,他的行为非常类似循环学习路径:
- 递归函数的介绍
 - 递归函数的特征
 
递归函数的介绍
如果一个函数内部调用自己本身,并且通过 条件控制及避免陷入死循环,那么我们称这个函数为递归函数。
- 基本使用
 
function f00(){console.log('普通函数......');foo(); //函数内部调用自己}//调用函数foo();-----------------------------------------------------------//递归函数;;基本样子,函数内部自己会调用自己配和条件function fn() {console.log('你好,128');fn()}fn();
- 注意:上述代码会导致foo一直重复被调用,会陷入死循环,浏览器会以报错形式终止执行
 
目标:递归是函数的高级用法,本质上函数自己调用自己,他的行为非常类型循环
递归是要加结束条件
特点:循环执行、自己调用自己,一定退出条件。
//递归函数特点:循环执行 、自己调用自己、一定退出条件;let i=9; //全局变量,在任务中function fn(){i++;//自增1,通过条件控制是否调用自己if(i<3){fn();}}fn();
递归函数的特性
目标: 能够说出递归函数的3个特性
递归函数有3个特征
- 重复执行
 - 调用自身
 【必须】要有条件控制,避免死循环
//利用递归要求1~100的和let s=0;i=0function foo(){//条件控制避免陷入死循环if(i >= 100) return;i++;s += 1foo();//自调用}foo();
DOM-节点操作事件流
目标: 掌握元素节点操作, 知道事件执行时的事件流动过程
本次学习元素节点创建、 复制、 插入、 删除等操作的方法, 元素节点的结构关系查找节点, 事件执行时的事件流动过程。
学习路径:
- 节点操作
 - 事件流
 
节点操作
掌握节点常见的增删改查操作
插入节点
即创造出一个新的网页元素, 再添加到网页内, 一般先创建节点, 然后插入节点
创建元素节点方法:
```javascript //创建一个新的元素节点 document.createElement(‘标签名’) //克隆一个已有的元素节点 元素.cloneNode(布尔值)
//1.创建新节点 // create:创建 // Element:元素对象 // 参数:标签字符串,写一个! // 返回:创建出新的dom字点(拥有属性和方法都是学过的!),目前只是存在与js层面,没有在页面中显示 let div = document.createElement(“div”) div.innerText = ‘我是一个新的div’ div.style.backgroundColor = ‘red’ // 2.插入dom节点 //从后添加:父元素节点appendChild(要添加的元素节点); let father = document.querySelector(‘.father’) father.appendChild(div)
-cloneNode会克隆出一个跟原标签一样的元素, 括号内传入布尔值1. 若为true,则代表克隆时会包含后代节点一起克隆2. 若为false,则代表克隆时不包含后代节点3. 默认为false```javascript// 3.克隆复制节点// 被克隆元素节点.cloneNode(Boolean值)// false:不会克隆后代,默认// true:也会克隆后代// 得到:dom节点(拥有都是学过属性和方法)let ul = document.querySelector("ul")let cp = ul.cloneNode(true);
插入节点
- 要想在界面看到, 还得插入到某个父元素中
 - 插入到父元素的最后一个子元素
 - 插入到父元素中某个子元素的前面
 
// 4.指定在某个元素的前面添加// 父元素的节点:.insertBefore(要添加的元素,添加这个元素的前面)let p = document.querySelector('p')father.insertBefore(cp, p)
案例-微博发布
<textarea name="" id="" cols="30" rows="10"></textarea><br><button>发布</button><ul><li>我要发布微博 </li></ul><script>//需求:用户输入内容后,点击发布按钮,UL列表里边有发布内容显示// 步骤:// 1.获取DOM节点let btn = document.querySelector("button")let ipt = document.querySelector("textarea")// 2.添加事件btn.addEventListener("click", function () {//用户点击后,获取输入框的内容let value = ipt.value //点播//把获取的内容,添加到一个新的dom里面,创建lilet li = document.createElement("li")li.innerText = value;//把新的dom添加到UL后面let ul = document.querySelector("ul")ul.appendChild(li);ipt.value = ' ';if (li = 'none') {alert('请输入内容')}})</script>
删除节点
若一个节点在页面中已不需要时, 可以删除它
在 JavaScript 原生DOM操作中, 要删除元素必须通过父元素删除
语法:
父元素.removeChold(要删除的元素)注: 如不存在父子关系则删除不成功
<ul><li>1</li><li>2</li><li class="last">3</li></ul><script>//删除点:// 父元素节点.removeChild(被删除的元素节点)let ul = document.querySelector("ul")let last = document.querySelector('.last')ul.removeChild(last);</script>
替换节点
替换节点是指把一个已经存在页面中的元素替换成新的元素
方法一语法:
父元素.replaceChild(新节点,被替换节点)
方法二语法:
父元素.innerHTML=内容
方法二的innerHTML本质上是修改双标签里面的内容, 只不过遇到标签也会解析成元素
// 语法:// 父元素.replaceChild(新节点,被替换的节点旧);// 元素:准备3个let ul = document.querySelector('ul')let last = document.querySelector('.last')let div = document.createElement('div')div.innerText = '我是新的节点'ul.replaceChild(div, last)//替换:属性学过 innerHTMLlet p = document.querySelector("p")p.innerHTML = '<h1>128</h1>'
查找节点
父子关系查找:
获取父节点方法
parentNode属性 返回最近一级的父节点 找不到返回为null
获取子节点
childNodes- 获得所有子节点、包括文本节点(空格、换行)、注释节点等
 
children- 仅获得所有元素节点 ```javascript //获取接点:获取页面中已经存在的节点,得到节点,通过方法获取; // 节点属性; // 节点添加事件监听; // 操作节点; // 查找节点:得到节点,dom数有父子关系;通过属性获取;
 
// 获取某个元素的父级节点;子元素.parentNode ====>dom节点(有关dom学习所有知识都看可以用) let li = document.querySelector(‘li’) console.log(li.parentNode.parentNode);
-第一个子节点和最后一个子节点<a name="f08ef9b3"></a>##### 案例-变色```html<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>// 需求:点击每个btn按钮,都需要让其所在整行变色;// 获取所有按钮let btn = document.querySelectorAll("tr button")// 添加点击事件for (let i = 0; i < btn.length; i++) {btn[i].addEventListener("click", function () {this.parentNode.parentNode.style.backgroundColor = 'red'})}</script>
查找兄弟的属性
previousSibling- 找到上一个兄弟节点,包含文本、注释等节点
 
prevuisElementSibling- 找到上一个兄弟元素,只找到元素
 
nextSibling- 找到下一个兄弟节点,包含文本、注释等节点
 
nextElementSibling- 找到下一个兄弟元素,只找到元素
 
事件流
学习路径:
- 事件流与两个阶段说明
 - 事件捕获和事件冒泡
 - 阻止事件流动
 - 事件委托
 
事件流与两个阶段说明
能够说出事件流经过的2个阶段
- 事件流指的是事件完整执行过程中的流动路径
 
说明: 假设页面里有个div, 当触发事件时, 会经历两个阶段, 分别是捕获阶段、 冒泡阶段
简单来说: 捕获阶段是 从父到子 冒泡阶段是从子到父
事件捕获和事件冒泡
- 事件冒泡是默认存在的
 - 它指的是: 当一个元素触发事件后, 会依次向上调用所有父级元素的同名事件
 - 事件捕获需要写对应代码才能看到效果
 - 代码:  
元素.addEventListener(事件名,回调函数,true) 说明:
- addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
 - 若传入false代表冒泡阶段触发,默认就是false
 - 若是用 L0 事件监听,则只有冒泡阶段,没有捕获
 
阻止事件流动
目标: 能够写出阻止事件流动的代码
- 因为默认就有冒泡模式的存在, 所以容易导致事件影响到父级元素
 - 若想把事件就限制在当前元素内, 就需要阻止事件流动
 - 阻止事件流动需要拿到事件对象
 - 语法:
事件对象.stopPropagation() - 此方法可以阻断事件流动传播, 不光在冒泡阶段有效, 捕获阶段也有效
 鼠标经过事件:
mouseover和mouseont会有冒泡效果mouseenter 和 mouseleave没有冒泡效果(推荐)
<div class="father"><div class="user"></div><div class="set"></div></div><script>// 需求:左侧,显示用户功能// 右侧 显示配置功能// 中间:显示时间功能let user = document.querySelector(".user");user.addEventListener("click", function (e) {console.log("大儿子,说疼");//事件对象,阻止冒泡// Propagation:传播 1e.stopPropagation();})let set = document.querySelector(".set");set.addEventListener("click", function (e) {document.write("小儿子喊,舒服")e.stopPropagation();});let father = document.querySelector(".father");father.addEventListener("click", function (e) {console.log("父亲打哪个儿子");e.stopPropagation();});// 阻止冒泡:从目标节点--->根节点流动路径,切断!// 大白话:</script>
事件委托
目标: 能够说出事件委托的好处
事件委托是利用事件流的特征解决一些开发需求的知识技巧
总结:
- 事件委托其实是利用事件冒泡的特点,给父级元素加事件
 事件对象.target 可以获得真正触发事件的元素
<body><div class="box"><button>1</button><button>2</button><button>3</button><button>4</button><button>5</button></div><script>let btn = document.querySelectorAll("button")for (let i = 0; i < btn.length; i++) {btn[i].addEventListener("click", function () {// 打印当前文本内容console.log(this.innerText);})}//优化写法:事件委托,不是直接注册btn;注册给父亲// 应用场景:遇到给一堆元素注册同样事件类型;全部获取,循环注册!// 找着堆元素公共级父级,把事件添加给、委托给父亲上,// 知道用户刚才触发时哪个节点就可以!/* let box = document.querySelector('.box')box.addEventListener('click', function (e) {console.log(e.target.innerText);}) */// 好处:只需要注册一个事件</script></body>
事件流小结
事件流与两个阶段说明
- 完整事件的流动过程
 - 捕获阶段 -> 真正触发事件的元素 -> 冒泡阶段
 
事件捕获和事件冒泡
- 事件捕获是从document一级一级往下到真正触发事件的元素
 - 事件冒泡是从真正触发事件的元素一级一级往上到document
 - 默认就有事件冒泡
 - 捕获必须用L2事件,并第三个参数给true
 
阻止事件流动
事件对象.stopPropgation()
事件委托
利用事件冒泡的特点把事件委托给父级元素
mouseenter和mousemover的区别
<body><div class="father"><div class="son">1</div></div><script>// mouseenter / mouseleave鼠标移入移出 focus blur不会冒泡//大部分都是默认在冒泡阶段执行;let son = document.querySelector(".son");son.addEventListener("mouseenter", function () {console.log("移入了son");})let father = document.querySelector(".father");father.addEventListener("mouseenter", function () {console.log("移入了father")})</script></body>
综合案例-表格渲染
<body><h3>添加课程</h3><div class="con">课程名称:<input type="text" class="title"> 课程难度: <input type="text" class="hard"><button class="add">添加</button></div><table><thead><tr><th width="80">序号</th><th>课程名</th><th>难度</th><th>操作</th></tr></thead><tbody><tr><td>1</td><td>HTML</td><td>初级</td><td><button>删除</button></td></tr><tr><td>2</td><td>CSS</td><td>初级</td><td><button>删除</button></td></tr><tr><td>3</td><td>JavaScript</td><td>中级</td><td><button>删除</button></td></tr></tbody></table><script>// -------------------------------------------1.add// 步骤:// 1.获取let add = document.querySelector(".add");let title = document.querySelector(".title");let hard = document.querySelector(".hard");let tbody = document.querySelector("tbody");// 2.添加点击事件add.addEventListener("click", function () {// 3.1 获取用户输入的内容let kecheng = title.value;let nandu = hard.value;// 3.2 先创建某些个dom节点,let tr = document.createElement("tr");// 3.3 把内容放入某些dom节点tr.innerHTML = `<td>${kecheng}</td><td>${nandu}</td><td><button>删除</button></td>`;// 3.4 添加到tbody后面:父级.appendChild(tr)tbody.appendChild(tr);// 4.置空title.value = "";hard.value = "";});// *****************************************************************************删除// 1.找相对适合父级tbody.addEventListener("click", function (e) {// 知道用户点击是哪个类型dom节点// tr td button e.target.tagNameif (e.target.tagName == "BUTTON") {let father = e.target.parentNode.parentNode;tbody.removeChild(father);}});</script></body>
