携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
前言
大白话说JS内容包括:DOM的一些操作,Promise相关, 微任务宏任务,作用域,变量提升,闭包,变量类型,深浅拷贝,原型和作用域链,后续争取把js重点都记录上,深入浅出。
DOM操作
Dom翻译过来就是文档对象模型,它是页面的一个层次化节点数(就是各种嵌套div,标签的树),咱们可以对这树进行常见的增删改查操作,来完善美化页面。
常见操作结点的方式:(非主要讲解内容,大致说下,可以按数组的API来理解)
说些Node类型的方法吧:
- appendChild(),跟数组的push方式一样,在在结点末尾添加一个结点
- insertBefore(),非末尾插入,在指定地方插入
- replaceChild(),可插入可替换,像数组的splice
- removeChild()和cloneNode(),见名知意,就不说了
当然,还有Document类型的,这个大伙就很熟悉了,像getElementById, getElementsByName, getElementsByTagName 大家都很熟悉就不说了(Element也不说了🤣)。说点它的实际应用操作吧
它的实际应用操作有:
- 使用文档碎片减少DOM操作
- 冒泡事件:stopPropagation()阻止向上冒泡
- 事件委托: 减少DOM请求次数
使用文档碎片减少DOM操作
假设在一个ul中,如果持续添加一百次插入 li 的DOM操作,那这个过程无疑是非常消耗内存的,这时不妨使用文档碎片 fragment ,将插入的100次操作存储在这,结束后在一并添加到ul中,这样DOM操作从原本的100次变为一次。
<ul id="list">
</ul>
<script src="index.js"></script>
const list = document.getElementById('list');
// 创建文档碎片,内容放在内存里,最后将结果一次插入list,减少DOM操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 5; i++) {
const item = document.createElement('li');
item.innerHTML = `事件${i}`;
// 每次插入都是一次DOM操作,消耗性能,所以先往文档碎片放入li,循环完后在插入list
// list.appendChild(item);
fragment.appendChild(item);
}
list.appendChild(fragment);
阻止冒泡事件
这一切的一切,还得从源头说起:
事件流描述的是从页面中接收事件的顺序,一共三个阶段:捕获阶段,目标阶段,冒泡阶段。一般事件在浏览器中处于冒泡阶段才被执行,如果想在捕获阶段就触发,可用addEventListener 方法,这个方法接收3个参数:要处理的事件名、处理函数和布尔值(true就表示在捕获阶段就触发)。
依旧举个栗子:当点击一个 li , 他没有绑定事件处理函数,那么他会往父级元素(一直往上找直到body)看有没有事件处理函数,有的话同样触发。
这样可以简便的为子元素绑定处理事件,但弊端也很明显:如果父元素有处理函数,即使你给子元素绑定了处理事件,那么在触发子元素的处理事件同时,还会向上冒泡在触发父元素的处理函数!!这就需要到 stopPropagation(); 阻止冒泡行为。
<div id="one">
<p id="p1">冒泡</p>
</div>
<hr>
<div id="two">
<p id="p4">冒泡</p>
<p id="p5">阻止</p>
</div>
const p3 = document.getElementById('p3');
const one = document.getElementById('one');
const body = document.body;
function bindEvent (elem, type, fn) {
elem.addEventListener(type, fn);
}
bindEvent(one, 'click', function () {
console.log("one的click");
})
bindEvent(body, 'click', function () {
console.log("body的click");
})
bindEvent(p3, 'click', function () {
console.log("p5的click");
})**
在没有绑定阻止冒泡时,给了body,第一个父元素和第三个子元素绑定处理事件,点击第三个会触发第三个和body的,点击第一个会触发父元素和body的。如果只想触发第三个事件绑定,可将p3的触发函数修改为:传入event,利用event下的 stopPropagation(); 方法。
bindEvent(p3, 'click', function (event) {
event.stopPropagation();
console.log("阻止冒泡");
})
事件委托
事件指的是:不在要发生的事件(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,监听子元素,来做出不同的响应。
例如在ul中有这么五个小 li ,要给他们绑定点击事件,可获取他们标签名后,利用slice方法对li的类数组转化为数组,在对其中的 li 添加注册事件。
<ul id="list">
<li>事件1</li>
<li>事件2</li>
<li>事件3</li>
<li>事件4</li>
<li>事件5</li>
<button id="btn">点击添加委托事件</button>
</ul>
const lis = document.getElementsByTagName('li');
listAray = Array.prototype.slice.call(lis);
listAray.forEach(li => {
addEvent(li, 'click', () => {
alert(li.innerHTML);
})
});
要是有500个li呢,那岂不是遍历绑定500次,这产生的事件监听器非常消耗内存。这时可以找到其父元素,设置事件监听器,利用 target 给 li 绑定事件监听。
// 绑定事件处理函数
function addEvent (elem, type, fn) {
elem.addEventListener(type, fn);
}
addEvent(list, 'click', (e) => {
const target = e.target;
if (target.nodeName === 'LI') {
alert(target.innerHTML);
}
})
btn.addEventListener('click', () => {
const li = document.createElement('li');
li.innerHTML = '新增绑定事件';
list.insertBefore(li, btn);
})
这样,即使不循环遍历 ul 下的 li ,也能通过对 ul 的绑定打印出子元素 li 的信息。
以上,就是常见的一些DOM实际应用操作,写的不好,多多包涵