上周,主要开发项目中APP的内嵌页,前端主要采用的是Zepto.js,其中有一个是显示电子书的目录列表,采用的是li,目录显示效果如下图,当点击更多目录时显示全部列表项。
目录结构如下:
<ul id="chapters">
<li><a href="#1">章节1</a></li>
<li><a href="#2">章节2</a></li>
<li><a href="#3">章节3</a></li>
<li><a href="#4">章节4</a></li>
</ul>
<a href="javascript:void(0);" id="more">更多目录</a>
当点击更多目录时,直接使用$("#chapters li").show()显示出所有列表项,在chrome仿真器中,一瞬间就可以显示出来,但在真机中,浏览器竟然出现假死的现象,大概会出现5s的卡顿,主要一个原因是有一些电子书有900多个章节。我尝试使用变量把DOM结果集缓存起来,还是会出现卡顿的现象。
翻看Zepto.js的源码,查看show()方法的实现:
show: function(){
return this.each(function(){
this.style.display == "none" && (this.style.display = '')
if (getComputedStyle(this, '').getPropertyValue("display") == "none")
this.style.display = defaultDisplay(this.nodeName)
})
}
show()会遍历每一个li,每遍历一个li,如果display属性为none,就会设置一次display属性,DOM树就会渲染一次。如果有900多个li,DOM就会渲染900多次,在PC上问题不大,但在手机上,由于内存较小,就会造成假死的现象。
所以,为了解决这个问题,需要保证每次点击只进行一次DOM渲染,这样才不会出现假死的现象。修改后的代码如下:
//chapters 为ajax获取得到的数据,格式为json
var chaptersLength = chapters.length, //列表项长度
allChapters = null, //保存全部目录
lessChapters = null, //保存最少显示的目录
hideMore = true, //是否隐藏
$template = '<% _.each(chapters, function(chapter) { %>' +
'<li><a href="<%= chapter.url %>" data-chapterid="<%= chapter.chapter_id %>">' +
'<%= chapter.chapter_title %></a></li><% });%>',//列表显示模板
$chapters = $("#chapters"), //目录
$more = $("#more"); //按钮
if (chaptersLength > 4) { //默认显示四个,大于四个时显示更多目录按钮并添加事件
allChapters = chapters;
for (var i = 0; i < 4; i++) {
lessChapters.push(chapters[i]);
}
renderChapter(lessChapters);
$more.show();
$more.on("click", function(){
if (hideMore) {
hideMore = false;
$more.text("正在加载");
setTimeout(function(){ //默认会有几毫秒的延时,主要是为了显示“正在加载”中的状态
renderChapter(allChapters);
$more.text("收起目录");
}, 0);
} else {
hideMore = true;
renderChapter(lessChapters);
$more.text("更多目录");
}
});
} else {
renderChapter(chapters);
}
/**
* 展示列表
*/
function renderChapter(chapters) {
//这里使用underscore.js _.template()方法
$chapters.html(_.template($template)({chapters:chapters}));
}
每次处理都是重新写一次DOM树,这样就不会出现卡死的现象。