首页 > 代码库 > DOM操作
DOM操作
? 页面上有个空的无序列表节点,用
<ul></ul>
表示,通过JavaScript动态往列表中插入 3 个<li>
,每个列表项的文本内容是列表项的插入顺序,取值 1, 2, 3;同时绑定click事件,单击依次输出1,2,3。
<ul class="js-container">
<!-- 动态添加内容 -->
</ul>
动态添加li
var containerDom = document.querySelector(‘.js-container‘),
itemDom = null;
for(let i = 1; i <= 3; i++) {
itemDom = document.createElement("li");
itemDom.innerText = i;
containerDom.appendChild(itemDom);
}
绑定事件
var containerDom = document.querySelector(‘.js-container‘),
itemDom = null;
for(let i = 1; i <= 3; i++) {
itemDom = document.createElement("li");
itemDom.innerText = i;
itemDom[i].addEventListener(‘click‘, function() {
alert(i); // alert(this.innerText);
});
containerDom.appendChild(itemDom);
}
需要注意: 上述使用let
局部作用域(使用闭包同样可以实现)!绑定事件使用addEventListener
,而没有使用内联事件onclick
。是因为内联事件是作为元素属性保存起来的,这些属性可以被覆盖,所以如果为同一个事件绑定了多个处理程序,那么最后一个处理程序会覆盖之前的。
增大数据量
? 如果将li的数量改为500,5000甚至更大呢?页面必然会出现卡顿或者直接卡死。
事件代理,减少事件数量
var containerDom = document.querySelector(‘.js-container‘),
itemDom = null;
for(let i = 1; i <= 500; i++) {
itemDom = document.createElement("li");
itemDom.innerText = i;
containerDom.appendChild(itemDom);
}
// 事件代理
containerDom.addEventListener(‘click‘, function(e){
const target = e.target;
if (target.tagName === ‘LI‘) {
alert(target.innerHTML);
}
});
DocumentFragement可以减少DOM操作
? 接口表示的是没有父节点的最小的文档对象。它被当做一个轻量版本的 Document
使用,用于存储已排好版的或尚未打理好格式的XML片段。可以使用document.createDocumentFragment
方法或者构造函数来创建一个空的 DocumentFragment.
? DocumentFragement通常用来创建一个文档片段,然后将创建的DOM元素插入到文档片段中,最后把文档片段插入到DOM树中。在DOM树中,文档片段会被替换为它所有的子元素。因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面重绘(reflow)(对元素位置和几何上的计算)。因此,使用文档片段DocumentFragement
通常会起到优化性能的作用。
var containerDom = document.querySelector(‘.js-container‘),
contentFragment = document.createDocumentFragment(),
itemDom = null;
// 先将li添加到contentFragment,已减少dom操作
for(let i = 1; i <= 500; i++) {
itemDom = document.createElement("li");
itemDom.innerText = i;
contentFragment.appendChild(itemDom);
}
containerDom.appendChild(contentFragment);
// 事件代理
containerDom.addEventListener(‘click‘, function(e){
const target = e.target;
if (target.tagName === ‘LI‘) {
alert(target.innerHTML);
}
});
分批处理,requestAnimationFrame平滑过渡
创建动画时,大家经常会想到使用setTimeout
或setInterval
。使用上述方式有这样几个问题:
- 动画区域或者页面已被隐藏,
setTimeout
或setInterval
仍被执行; - 大多数计算机显示器以60Hz的速率刷新,这基本上意味着每秒重新绘制60次。为了得到最平滑的动画,需要设置最佳间隔是1000ms / 60或约17ms,但这不能覆盖全部浏览器;
- 延迟毫秒数并不意味着该毫秒后被执行,仅表示其进行排队。如果UI线程很忙,可能会处理用户操作,那么该代码将不会立即执行;
window.requestAnimationFrame(callback) 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。window.cancelAnimationFrame()
来取消这个回调函数。
const containerDom = document.querySelector(‘.js-container‘);
const total = 5000, // 5000个li
batchSize = 50, // 每一个批次执行50个
batchCount = Math.ceil(total / batchSize); // 批次数
let batchDone = 0; // 已经完成的批处理个数
/**
* 批次插入li
*/
function appendItems() {
let contentFragment = document.createDocumentFragment(),
itemDom = null;
for(let i = 1; i <= batchSize; i++) {
itemDom = document.createElement("li");
itemDom.innerText = (batchDone * batchSize) + i;
contentFragment.appendChild(itemDom);
}
containerDom.appendChild(contentFragment);
batchDone++;
// 调用下一批次
doBatchAppend();
}
/**
* 平滑插入各个批次
*/
function doBatchAppend() {
if (batchDone < batchCount) {
// 无需设置时间
window.requestAnimationFrame(appendItems);
}
}
doBatchAppend();
// 绑定事件
containerDom.addEventListener(‘click‘, function(e){
const target = e.target;
if (target.tagName === ‘LI‘) {
alert(target.innerHTML);
}
});
注意: requestAnimationFrame()
存在一定的兼容性问题
(function() {
if (window.requestAnimationFrame) {
return;
} else {
var lastTime = 0;
var vendors = [‘ms‘, ‘moz‘, ‘webkit‘, ‘o‘];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+‘RequestAnimationFrame‘];
window.cancelAnimationFrame = window[vendors[x]+‘CancelAnimationFrame‘]
|| window[vendors[x]+‘CancelRequestAnimationFrame‘];
}
// 使用setTimeout模拟实现
if (!window.requestAnimationFrame){
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}
}());
参考地址:
http://creativejs.com/resources/requestanimationframe/
https://www.nczonline.net/blog/2011/05/03/better-javascript-animations-with-requestanimationframe/
DOM操作