DOM操作

来源:互联网 发布:天龙抢号软件 编辑:程序博客网 时间:2024/06/05 19:33

​ 页面上有个空的无序列表节点,用<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平滑过渡

创建动画时,大家经常会想到使用setTimeoutsetInterval。使用上述方式有这样几个问题:

  • 动画区域或者页面已被隐藏,setTimeoutsetInterval仍被执行;
  • 大多数计算机显示器以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/

1 0
原创粉丝点击