JS appendHTML实现加载更多评论的方法

目录
文章目录隐藏
  1. 如何实现 appendHTML 方法?
  2. 有木有 prependHTML 方法?
  3. 结语

JS 中有很多基本 DOM 方法,例如 createElement, parentNode 等,其中,appendChild 方法是相当地常用与熟知,可谓是 DOM 节点方法中的“无人不识君”!

appendChild 的作用是在指定元素节点的最后一个子节点之后添加节点。放在 HTML 中解释就是不是 append 节点,而是 append 构成节点的 HTML 字符代码。

例如,我们点击“更多评论”按钮,需要 ajax 加载评论相关 HTML 代码并插入到页面中,此时,appendChild 显然就不如 appendHTML 方法来得方便。

JS appendHTML 实现加载更多评论的方法

如何实现 appendHTML 方法?

如果是纯粹没有脚本等行为的容器。 我们直接 innerHTML 拼接就可以了。例如,append 一个图片 HTML 片段,我们可以:

container.innerHTML = container.innerHTML + '<img src="mybj123.jpg" />';

当然,这个大家都懂的,这只是一个铺垫,只在特殊情况用用(单纯 HTML 处理),如果真要构造一个通用的 appendHTML 方法,innerHTML 拼接显然是没有市场的。

考虑到容器的原 HTML 极可能包含事件,因此,实现 append 效果的时候一定不能干扰之前的内容,这就需要借助 appendChild 方法。我们把 HTML 字符串转换成节点,然后通过 appendChild 方法载入进去。

如何将 HTML 字符串(假设字符串变量名为 html)转换成节点呢?如下操作即可:

// 创建 div 节点
var div = document.createElement("div");
// 装载 html 字符串
div.innerHTML = html;
// 此时 div.childNodes 就是我们需要的节点了!
return div.childNodes;

下面要做的就是将这些节点 append 进去,下面是我们自然理解的实现,遍历:

var nodes = div.childNodes;
for (var i=0, i<nodes.length; i++) {
   // 容器 container 加载克隆的节点 - 克隆的作用是保证 nodes 的完整
   container.appendChild(nodes[i].cloneNode(true)); 
}

上面代码功能虽然实现,但是性能却是相当的不好,尤其是在低版本 IE 下,缺少优化的机制①,每次 appendChild 造成的回流与渲染会让浏览器 high 到叫的。

对于 DOM 节点插入,大家应该都熟知“文档片段优化法”。具体来讲,就是使用 document.createDocumentFragment()创建一个文档片段,然后,把节点一个一个 append 到这个片段中,回到页面上的时候,直接 append 这个文档片段就可以了-只有一次。

代码解释就是:

var nodes = div.childNodes
    // 我就是那把快刀,
    , fragment = document.createDocumentFragment();

for (var i=0, length=nodes.length; i<length; i+=1) {
   // 文档片段加载克隆的节点
   fragment.appendChild(nodes[i].cloneNode(true)); 
}
// 一刀来个痛快
container.appendChild(fragment);

于是,我们在 HTML 元素原型上扩展,可以让高端点的浏览器(IE9+, …)都有了 appendHTML 方法。

HTMLElement.prototype.appendHTML = function(html) {
    var divTemp = document.createElement("div"), nodes = null
        // 文档片段,一次性 append,提高性能
        , fragment = document.createDocumentFragment();
    divTemp.innerHTML = html;
    nodes = divTemp.childNodes;
    for (var i=0, length=nodes.length; i<length; i+=1) {
       fragment.appendChild(nodes[i].cloneNode(true));
    }
    this.appendChild(fragment);
    // 据说下面这样子世界会更清净
    nodes = null;
    fragment = null;
};

有木有 prependHTML 方法?

appendHTML 是在容器的最后加载 HTML,那可不可以实现在容器前面加载 HTML 的方法呢,姑且命名为 prependHTML。

自然可以,差别在于不是借助 appendChild, 而是 insertBefore 方法。只要做如下修改就可以了:

this.appendChild(fragment);
// 变成↙
this.insertBefore(fragment, el.firstChild);

完整代码如下(单纯的兼容方法):

var prependHTML = function(el, html) {
    var divTemp = document.createElement("div"), nodes = null
        , fragment = document.createDocumentFragment();

    divTemp.innerHTML = html;
    nodes = divTemp.childNodes;
    for (var i=0, length=nodes.length; i<length; i+=1) {
       fragment.appendChild(nodes[i].cloneNode(true));
    }
    // 插入到容器的前面 - 差异所在
    el.insertBefore(fragment, el.firstChild);
    // 内存回收?
    nodes = null;
    fragment = null;
};

为节省代码,前加载 HTML 可以不使用 prependHTML, 而是还是命名为 appendHTML 方法,不过通过另外一个参数控制加载的位置。处理如下:

var appendHTML  = function(el, html, where) {
    where = where || "bottom";
    // where 参数 bottom(默认)或者 before
    where !== "before"? el.appendChild(fragment) : el.insertBefore(fragment, el.firstChild);;

MooTools 框架中的 DOM 方法很多都是这个思路。

备注

① 大部分浏览器对元素几何改变时候的重排做了优化。据说是这样子,一定时间内本应多次重排的改变,浏览器会 hold 住,仅一次重排。其中如果使用分离的一步处理过程,例如计时器,依然多次重排。例如,当我们应用 transition 动画的时候,希望从 0px 变化到 100px. 你如果如下代码:

dom.style.left = "0px";
dom.style.left = "100px";

元素是不会从 0~100 像素动画的,因为现代浏览器有自己的优化机制,它只会处理后面的 dom.style.left = “100px”,使用定时器可以阻断这种优化,实现我们想要的过渡动画效果,如下:

dom.style.left = "0px";
setTimeout(function() { dom.style.left = "100px"; }, 20);

补充

除了上述提到的方法,还有一个 insertAdjacentHTML 方法,因此,这使得目前使用 insertAdjacentHTML 实现 HTML 片段插入效果成为了可能。

语法如下:

element.insertAdjacentHTML(position, html);

position 是相对于 element 元素的位置,并且只能是以下的字符串之一:

beforebegin:在 element 元素的前面。

afterbegin:在 element 元素的第一个子元素前面。

beforeend:在 element 元素的最后一个子元素后面。

afterend:在 element 元素的后面。

html 是字符串被解析成 HTML 或 XML 插入到 DOM 树中。

其中 beforeend 参数就是我们需要的 appendHTML 效果。

相关代码如下:

var $ = function(id) {
    return {
        0: document.getElementById(id),
        length: 1,
        click: function(fn) {
            this[0].onclick = fn;
        },
        insertAdjacentHTML: function(position, html) {
            this[0].insertAdjacentHTML(position, html);
        }
    }
};

$("commentMore").click(function() {
    var xhr, self = this;
    if (this.ajaxing) return;
    if (window.XMLHttpRequest) {
        xhr = new window.XMLHttpRequest();
    } else {
        xhr = new ActiveXObject("Microsoft.XMLHttp");    
    }
    // 提示
    this.innerHTML = '加载中...';
    // 阻止二次点击
    this.ajaxing = true;
    // ajax go go go!
    xhr.open("get", this.getAttribute("data-url"), true);
    xhr.send(null);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200 && xhr.responseText) {
                $("commentUl").insertAdjacentHTML("beforeend", xhr.responseText);    
                self.parentNode.innerHTML = '<span class="g9">全部加载完毕</span>';
                self = null;
            } else {
                self.innerHTML = '加载失败!点击重试~';
                self.ajaxing = false;
            }            
        }
    }
    return false;
});

结语

以上就是今天的全部内容,希望大家喜欢,感谢阅读,一个点赞一个小小的转发就是对码云笔记最大的认可。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » JS appendHTML实现加载更多评论的方法

发表回复