就是这么标题党哇咔咔咔~~
其实只是突然想总结的一些实用小技巧吧,以后想到什么再补充。
经常遇到比如有一列<li></li>需要绑定点击事件,想想就很容易:
<ul id="ul">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
var oUl = document.getElementById('ul');
var aLi = oUl.getElementByTagName('li');
aLi.addEventListener('click',function(){
alert(aLi.innerHTML);
})
</script>可是,如果,我有100个li,那是要在100个li都绑定事件监听?
还是不要吧...
那么事件委托就可以上场了,顾名思义就是把这些事件委托给别人来做,这个别人一般为它的父元素。
<ul id="ul">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
var oUl = document.getElementById('ul');
// 事件侦听器绑定到整个容器上
oUl.addEventListener('click', function(e) {
if (e.target && e.target.nodeName === 'LI') {
var aLi = e.target;
alert(aLi.innerHTML);
}
});
</script>jquery的事件委托可以参考我的另一篇博文:http://anchen1204.github.io/jqcli.html
window对象的resize、scroll事件,文字输入、自动完成的keyup事件,拖拽时的mousemove事件,它们有个共同点:会频繁地被触发,超乎你的想象。结果可能严重到导致频繁执行DOM操作至浏览器崩溃。
这个时候就要讨论下这问题了,什么是函数防抖?
// 简单的防抖动函数
function debounce(func, wait, immediate) {
// 定时器变量
var timeout;
return function() {
// 每次触发 scroll handler 时先清除定时器
clearTimeout(timeout);
// 指定 xx ms 后触发真正想进行的操作 handler
timeout = setTimeout(func, wait);
};
};
// 实际想绑定在 scroll 事件上的 handler
function realFunc(){
console.log("Success");
}
// 采用了防抖动
window.addEventListener('scroll',debounce(realFunc,500));
// 没采用防抖动
window.addEventListener('scroll',realFunc);所以,就是500ms 内没有连续触发两次 scroll 事件,那么才会触发我们真正想在 scroll 事件中触发的函数,这就是函数防抖的原理了。
上面说了函数防抖,有一种情况可能就有点问题,比如我们下面要讨论的图片懒加载。当然本意并不是希望用户滑到某个点后,停了一段时间,才把他滑过和可视区域的图片加载进来,这是函数防抖的方式就不合适了。
于是说到函数节流。
与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件。
// 简单的节流函数
function throttle(func, wait, mustRun) {
var timeout,
startTime = new Date();
return function() {
var context = this,
args = arguments,
curTime = new Date();
clearTimeout(timeout);
// 如果达到了规定的触发时间间隔,触发 handler
if(curTime - startTime >= mustRun){
func.apply(context,args);
startTime = curTime;
// 没达到触发间隔,重新设定定时器
}else{
timeout = setTimeout(func, wait);
}
};
};
// 实际想绑定在 scroll 事件上的 handler
function realFunc(){
console.log("Success");
}
// 采用了节流函数
window.addEventListener('scroll',throttle(realFunc,500,1000));所以,就算500ms 内连续触发n次 scroll 事件,但保证我们的事件在1000ms内还是会被处理一次,这就是节流函数的原理啦。
关于以上两点的可见:http://www.codeceo.com/article/web-high-performance-scroll.html
另外补充一下,有个神奇的css属性叫pointer-events: none
之前用到的场景是头部有个酷炫的canvas动画,然后下面有优惠券需要点击,于是乎把canvas动画层设置这个属性,鼠标对页面的操作就不受到影响啦。
发现天猫首页滚动时,给body也加了这个属性,这是为了防止你的鼠标在滚动的时候停在页面上,会不断触发页面元素可能绑定的hover事件。
关于这个属性更多的文章:http://www.zhangxinxu.com/wordpress/2014/01/pointer-events-none-avoiding-unnecessary-paints/
另外,张大大还有另一篇讲关于这个属性对性能的提升讨论:http://www.zhangxinxu.com/wordpress/2014/01/pointer-events-none-avoiding-unnecessary-paints/ ,非常有趣,值得一读,也值得去实践。
懒加载就是lazyload(这是一句废话)。比较常见于图片按需加载,几乎很容易就可以在网上找到例子,比如前东家京东的各种页面。当这些资源不在浏览器的可视区域时,先不用加载,等到适当的时候再去加载资源,避免网页打开时间过长,页面load太久影响用户体验。
实现原理:
1.为<img>标签设置一个暂存图片URL的自定义属性(例如data-url)存储图片实际地址,图片原本src里用一张1*1或者尺寸小的图统一代替,当图片出现在视口时,再将data-url的值赋给图片的src属性;
2.怎么判断“适当的时候”,这里只以竖直方向作为判断。
屏幕可视窗口大小:
原生方法:
window.innerHeight 标准浏览器及IE9+ document.documentElement.clientHeight 标准浏览器及低版本IE标准模式 document.body.clientHeight 低版本混杂模式
jQuery方法: $(window).height()
浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
原生方法:
window.pagYoffset——IE9+及标准浏览器 document.documentElement.scrollTop 兼容ie低版本的标准模式 document.body.scrollTop 兼容混杂模式;
jQuery方法:$(document).scrollTop();
获取元素的位置信息
jQuery:$(o).offset().top元素距离文档顶的距离
判断元素进入可视区域
元素在文档的高度跟浏览器窗口与(滚动条的高度+屏幕可视窗口的高度)相等即进入可视区域。
分享个插件http://www.appelsiini.net/projects/lazyload
预加载就是与懒加载相反的东西(这不是废话)。它是牺牲服务器前端性能来换取更好的用户体验,字面意思就是浏览器把图片下到本地,同样的src就会使用缓存。
function loadImage(url,callback) {
var img = new Image();
img.src = url;
img.onload = function(){
img.onload = null;
callback.call(img);
}
}使用场景比如,头部动画元素较多或者有某个比较大,需要让图片预加载,以免影响动画效果,相信也不希望动画在播放的同时图片还在load吧。
比如我其中一个项目这么写:
/*头部动画图片预加载*/
function preLoad(){
var iCount=0,
img1 = new Image(),
img2 = new Image(),
img3 = new Image(),
img4 = new Image(),
img5 = new Image(),
img6 = new Image();
var arr = [img1,img2,img3,img4,img5,img6];
for(var i=0;i<arr.length;i++){
arr[i].onload=function(){
iCount++;
if(iCount==6){
$('.banner').addClass('ani-banner');
}
}
}
img1.src='地址';
img2.src='地址';
img3.src='地址';
img4.src='地址';
img5.src='地址';
img6.src='地址';
}
$(function(){
preLoad();
});首先复习一下浏览器中访问一个网址的页面呈现过程:
1.浏览器首先获取html页面;
2然后解析它的Dom结构;
3.接着解析css(css+Dom每个节点的位置和大小等几何信息,layout);
4.css paint(绘制成位图);
5.传输到GPU渲染到屏幕。
重排和重绘就发生在第3和第4步。
所以,在解析css开始的步骤,修改的css元素是在越前的步骤解析的话会越耗性能,因为它及其它后面的步骤将会被重新修改一遍。 换句话来说,Dom操作触发频繁reflow(layout)和repaint,layout发生在repaint之前,相对会造成更多性能损坏。
那么,哪些元素会影响layout和paint?
举些我们很常用的例子:
影响layout: margin/padding/width/height/display/border/top/left/right/bottom/position/font-size/text-align/line-height/font-weight/font-family/overflow...
影响repaint:
color/border-style/visibility/backgrounf/text-decoration/border-radius/outline/box-shadow/backgrounf-size...
注意:
设置动画元素为absolute或fixed:
竟然被问住了,然后十分钟之后想起来我....
也写在这里吧,之前有探讨过一篇关于web渲染及硬件加速,其实就有提到动画性能的问题。
曾经遇到一个移动端问题,做的一个蝴蝶节活动,有比较多的入场的动画,然后做好后设计觉得可以更花哨,要求加上一大两小三只蝴蝶在循环infinite动画,我觉得会比较耗性能阻止未果后,还是加上去。
然后测试上线时,6p微信崩了..。
后来,动画不能砍,设计会跟我拼命,所以我只能再看看。
检查是不是我的dom尺寸太大导致绘制时造成内存的溢出...
把蝴蝶的循环位移left啊这些位移直接交给translate3d来做,开启硬件加速,但是不能滥用,会耗电...
检查动画是否占文档流,是的话改为绝对定位,减少重排和重绘...
把图片重新再压缩一遍...
...其他还做了什么我忘了,最后没啥问题了
另外,如果需要些js动画的,一般会用setTimeout,但是还有另外一个为动画而生的,叫requestAnimationFrame,比如http://anchen1204.github.io/demo/spball/ball-10.html,这里就使用了这个东西。好处在于setTimeout可能会出现掉帧卡顿现象,因为它在js内执行,js是单线程可能会有阻塞。而requestAnimationFrame相当于是把动画交给浏览器去执行,浏览器会基于当前页面情况来自行决定最佳的帧速率,从而更合理地使用CPU。但是,它兼容Android4.4及以上,IE10及以上,兼容性其实跟css3动画差不多,所以可能要根据需求配合setTimeout做降级处理。
未完待续...