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=0
function foo(){
//条件控制避免陷入死循环
if(i >= 100) return;
i++;
s += 1
foo();//自调用
}
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里面,创建li
let 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)
//替换:属性学过 innerHTML
let 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:传播 1
e.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.tagName
if (e.target.tagName == "BUTTON") {
let father = e.target.parentNode.parentNode;
tbody.removeChild(father);
}
});
</script>
</body>