实战DHTML性能优化,改善自定义下拉框控件
先介绍一下本文演示用的代码,在我的项目中要实现一个自定义样式的下拉框,使用自定义的下拉箭头和自定义颜色的边框来取代系统默认的select。要实现这个功能就需要通过table模拟实现。那么我使用一个js方法来实现这个模拟过程,并封装为initSelect(id, width, height)方法。
并提供setSelect(id, index)进行某选项的选中,以及其他一些方法,全部功能至少同时兼容IE和FireFox。
因为当初写的比较随意,代码全部完成后,发现性能相当低下。大约出生日期这样的3个下拉框需要几百毫米的时间。
整优化过程我分3次进行
第一步
优化的技巧之一就是优先优化循环体,我把循环体内的不良做法优化,例如
本步产生代码initSelect2,祥见包内的s.js,下同
第二步
通过上面一个很小的改动,可以节省一些时间了,但是不会有什么大的改观,离我们优化要达到的目标相去甚远。那么现在静下来想一下,应该修改一下逻辑了
由于我们initSelect采用的方式是通过createElement创建TR,然后添加到table的rows集合中,由于创建和解析html都需要时间,所以效率较差,下面我们改良为数组方式
这样DOM只需解析一次HTML,取得了很好的效果,速度提高了3倍,从300ms降到几十毫秒。
本步产生代码initSelect3
第三步
那么还可以进一步优化吗,答案是肯定的。我们前两步的重点是循环体,现在该着眼大局了,由于整个方法体内仍然多次使用了innerHTML,这是一个非常慢的操作,微软非常推荐使用innerText来尽可能代替innerHTML,但是显然不适合此处。而且必须要指出的是innerText不属于W3C标准,因而不被fireFox支持。
我前面的一些随便中的DHTML技巧里已经指出了insertAdjacentElement会比insertAdjacentHTML效果好,但是这次我们决定抛弃insertAdjacentElement而使用insertAdjacentHTML,因为这样以来我们可以把HTML解析降低为一次,所以我们把多个节点使用一个insertAdjacentHTML完成,而不是insertAdjacentElement配合多次innerHTML赋值。采取这些调整以后,我们的代码再次得到提速,虽然效果不很明显,但是仍有超过10%的改善。
本步产生代码initSelect4
参考时间:
initSelect 313
initSelect2 297
initSelect3 140
initSelect4 94
后记
其实这个方法在项目开发初期就有了,当时使用的是
如果诸位需要使用这个自定义下拉框,只需select.js和s.css,而优化演示请看demo1中的文件(代码不完整,只做演示)
全文完
先介绍一下本文演示用的代码,在我的项目中要实现一个自定义样式的下拉框,使用自定义的下拉箭头和自定义颜色的边框来取代系统默认的select。要实现这个功能就需要通过table模拟实现。那么我使用一个js方法来实现这个模拟过程,并封装为initSelect(id, width, height)方法。
并提供setSelect(id, index)进行某选项的选中,以及其他一些方法,全部功能至少同时兼容IE和FireFox。
因为当初写的比较随意,代码全部完成后,发现性能相当低下。大约出生日期这样的3个下拉框需要几百毫米的时间。
整优化过程我分3次进行
第一步
优化的技巧之一就是优先优化循环体,我把循环体内的不良做法优化,例如
for(var i=0;i<sel.options.length;i++)
--》
for(var i=0,j=a.length;i<j;i++)
sel.options[i].value;
--》
a[i].value;
本步产生代码initSelect2,祥见包内的s.js,下同
第二步
通过上面一个很小的改动,可以节省一些时间了,但是不会有什么大的改观,离我们优化要达到的目标相去甚远。那么现在静下来想一下,应该修改一下逻辑了
由于我们initSelect采用的方式是通过createElement创建TR,然后添加到table的rows集合中,由于创建和解析html都需要时间,所以效率较差,下面我们改良为数组方式
var arr = new Array(a.length);
for (var i=0,j=a.length;i<j; i++)
{
arr[i] = "<tr><td title='" + a[i].text + "' value='" + a[i].value + "'>" + a[i].text + "</td></tr>";
}
oCell.innerHTML = "<div id=\"O" + id + "\" class=\"select\" onselectstart=\"return false\" onmouseover=\"HoverOptions(event);\" onmouseout=\"OutOptions(event);\" onclick=\"ClickOptions(event, 'O" + id + "');\">\
<table cellsapcing=\"0\" cellspadding=\"3\" border=\"0\" width=100%>\
<tbody>" + arr.join() + "</tbody>\
</table>\
</div>";
这样DOM只需解析一次HTML,取得了很好的效果,速度提高了3倍,从300ms降到几十毫秒。
本步产生代码initSelect3
第三步
那么还可以进一步优化吗,答案是肯定的。我们前两步的重点是循环体,现在该着眼大局了,由于整个方法体内仍然多次使用了innerHTML,这是一个非常慢的操作,微软非常推荐使用innerText来尽可能代替innerHTML,但是显然不适合此处。而且必须要指出的是innerText不属于W3C标准,因而不被fireFox支持。
我前面的一些随便中的DHTML技巧里已经指出了insertAdjacentElement会比insertAdjacentHTML效果好,但是这次我们决定抛弃insertAdjacentElement而使用insertAdjacentHTML,因为这样以来我们可以把HTML解析降低为一次,所以我们把多个节点使用一个insertAdjacentHTML完成,而不是insertAdjacentElement配合多次innerHTML赋值。采取这些调整以后,我们的代码再次得到提速,虽然效果不很明显,但是仍有超过10%的改善。
本步产生代码initSelect4
参考时间:
initSelect 313
initSelect2 297
initSelect3 140
initSelect4 94
后记
其实这个方法在项目开发初期就有了,当时使用的是
for(var i=0;i<sel.options.length;i++)
{
td.innerHTML += "<tr><td>" + sel.options[i].text + "</td></tr>";
}
结果当时一个50项的select在我的3g cpu上竟然需要500ms以上的加载时间,这次调优,我重写了该方法,并分步进行了优化,希望能给DHTML开发者们带来一些启示。
如果诸位需要使用这个自定义下拉框,只需select.js和s.css,而优化演示请看demo1中的文件(代码不完整,只做演示)
全文完