总结:从渲染机制谈script标签的位置是否会影响首屏时间

看了文章 【JS 一定要放在 Body 的最底部么?聊聊浏览器的渲染机制】,挺有感触,内容比较长,把总结部分摘要下:

浏览器的渲染过程:

  1. Create/Update DOM And request css/image/js:浏览器请求到HTML代码后,在生成DOM的最开始阶段(应该是 Bytes → characters 后),并行发起css、图片、js的请求,无论他们是否在HEAD里。注意:发起 js 文件的下载 request 并不需要 DOM 处理到那个 script 节点,比如:简单的正则匹配就能做到这一点,虽然实际上并不一定是通过正则:)。这是很多人在理解渲染机制的时候存在的误区。
  2. Create/Update Render CSSOM:CSS文件下载完成,开始构建CSSOM
  3. Create/Update Render Tree:所有CSS文件下载完成,CSSOM构建结束后,和 DOM 一起生成 Render Tree。
  4. Layout:有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置。
  5. Painting:Layout后,浏览器已经知道了哪些节点要显示(which nodes are visible)、每个节点的CSS属性是什么(their computed styles)、每个节点在屏幕中的位置是哪里(geometry)。就进入了最后一步:Painting,按照算出来的规则,通过显卡,把内容画到屏幕上。

以上五个步骤前3个步骤之所有使用 “Create/Update” 是因为DOM、CSSOM、Render Tree都可能在第一次Painting后又被更新多次,比如JS修改了DOM或者CSS属性。

Layout 和 Painting 也会被重复执行,除了DOM、CSSOM更新的原因外,图片下载完成后也需要调用Layout 和 Painting来更新网页。

继续阅读 总结:从渲染机制谈script标签的位置是否会影响首屏时间

targetTouches、touches、changedTouches的具体区别

  • touches: 当前屏幕上所有触摸点的集合列表
  • targetTouches:  绑定事件的那个结点上的触摸点的集合列表
  • changedTouches:  触发事件时改变的触摸点的集合

举例来说,比如div1, div2只有div2绑定了touchstart事件,第一次放下一个手指在div2上,触发了touchstart事件,这个时候,三个集合的内容是一样的,都包含这个手指的touch,然后,再放下两个手指一个在div1上,一个在div2上,这个时候又会触发事件,但changedTouches里面只包含第二个第三个手指的信息,因为第一个没有发生变化,而targetTouches包含的是在第一个手指和第三个在div2上的手指集合,touches包含屏幕上所有手指的信息,也就是三个手指。

为了方便记忆,做了个图:

touch

浮点计算 — 加减乘除

浮点计算一直有bug,项目用到了,在网上找了套能用的,记录下。。。

/**
 ** 乘法函数,用来得到精确的乘法结果
 ** 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
 ** 调用:accMul(arg1,arg2)
 ** 返回值:arg1乘以 arg2的精确结果
 **/
function accMul(arg1, arg2) {
    var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
    try {
        m += s1.split(".")[1].length;
    }
    catch (e) {
    }
    try {
        m += s2.split(".")[1].length;
    }
    catch (e) {
    }
    return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}

/**
 ** 除法函数,用来得到精确的除法结果
 ** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
 ** 调用:accDiv(arg1,arg2)
 ** 返回值:arg1除以arg2的精确结果
 **/
function accDiv(arg1, arg2) {
    var t1 = 0, t2 = 0, r1, r2;
    try {
        t1 = arg1.toString().split(".")[1].length;
    }
    catch (e) {
    }
    try {
        t2 = arg2.toString().split(".")[1].length;
    }
    catch (e) {
    }
    with (Math) {
        r1 = Number(arg1.toString().replace(".", ""));
        r2 = Number(arg2.toString().replace(".", ""));
        return (r1 / r2) * pow(10, t2 - t1);
    }
}

/**
 ** 减法函数,用来得到精确的减法结果
 ** 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
 ** 调用:accSub(arg1,arg2)
 ** 返回值:arg1加上arg2的精确结果
 **/
function accSub(arg1, arg2) {
    var r1, r2, m, n;
    try {
        r1 = arg1.toString().split(".")[1].length;
    }
    catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split(".")[1].length;
    }
    catch (e) {
        r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度
    n = (r1 >= r2) ? r1 : r2;
    return ((arg1 * m - arg2 * m) / m).toFixed(n);
}

/**
 ** 加法函数,用来得到精确的加法结果
 ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
 ** 调用:accAdd(arg1,arg2)
 ** 返回值:arg1加上arg2的精确结果
 **/
function accAdd(arg1, arg2) {
    var r1, r2, m, c;
    try {
        r1 = arg1.toString().split(".")[1].length;
    }
    catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split(".")[1].length;
    }
    catch (e) {
        r2 = 0;
    }
    c = Math.abs(r1 - r2);
    m = Math.pow(10, Math.max(r1, r2));
    if (c > 0) {
        var cm = Math.pow(10, c);
        if (r1 > r2) {
            arg1 = Number(arg1.toString().replace(".", ""));
            arg2 = Number(arg2.toString().replace(".", "")) * cm;
        } else {
            arg1 = Number(arg1.toString().replace(".", "")) * cm;
            arg2 = Number(arg2.toString().replace(".", ""));
        }
    } else {
        arg1 = Number(arg1.toString().replace(".", ""));
        arg2 = Number(arg2.toString().replace(".", ""));
    }
    return (arg1 + arg2) / m;
}

Google JavaScript代码风格指南

原文:http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml

每个风格点都有一个展开/收起按钮以便你可以得到更多的信息:.
你可以将全部展开或收起:


全部展开/全部收起

背景

JavaScript是一门客户端脚本语言,Google经常用它来晒优越,本文档列出了一些在做JS项目时需要注意的地方。都是高富帅整理的,还望各位屌丝们有时间都好好看看多学学,别对这个世界太消极了!(译者注:所谓的客户端呢就是浏览器或本地软件环境,市面上所有的浏览器都支持JS,JS发展到现在统一WEB端再进军移动互联网后,相信在一段时间内都是不可被轻易替代的语言,所以有兴趣的同学们可以多关注一下)

JavaScript语言规范

变量(var)

每个变量声明都要加上var关键字噢。

常量

常量命名用类似NAMES_LIKE_THIS这样的形式。没事干了可以用@const来标记它是常量,但永远不要用const关键字来进行常量声明。

分号


每一语句的结尾都要加上分号噢。

继续阅读 Google JavaScript代码风格指南

xmlHttpRequest对象的一些总结

总结,五个步骤:

1.创建XMLHttpRequest对象。

2.设置连接信息 xmlhttp.open(请求方式,url,是否同步);

3.注册回调函数 xmlhttp.onreadystatechange = 函数名 (函数名后面不要加括号); 

4.发送数据。xmphttp.send(data);

5.接收响应数据

readyState 属性存有服务器响应的状态信息。每当 readyState 改变时,onreadystatechange 函数就会被执行。

状态描述
0 请求未初始化(在调用 open() 之前)
1 请求已提出(调用 send() 之前)
2 请求已发送(这里通常可以从响应得到内容头部)
3 请求处理中(响应中通常有部分数据可用,但是服务器还没有完成响应)
4 请求已完成(可以访问服务器响应并使用它)

继续阅读 xmlHttpRequest对象的一些总结

Javascript判断鼠标滚动方向

做项目的时候,有时候需要判断鼠标滚轮的滚动方向,很常见的一个使用场景就是用js去模拟滚动条,判断的方法很简单:
IE、chrome、opera、safari 都使用 mousewheel 事件,用传统的事件绑定就 OK;
Firefox 又玩个性,它要使用 DOMMouseScroll 事件,并用 addEventListener 方法绑定;

// firefox
if(Element.addEventListener){
    Element.addEventListener('DOMMouseScroll', function(event){
        event.target.innerHTML = event.detail; //滚动的值
    }, false);
}

// ie & chrome & opera & safari
Element.onmousewheel = function(event) {
    event = event || window.event;
    Element.innerHTML = event.wheelDelta; //滚动的值
};

具体的值其实我们不在乎,关键是能判断好方向就行了,Firefox 的 addEventListener 方法里有一个布尔值参数,它的作用是判断一个绑定了不同事件的元素,用什么顺序去执行这些事件:

// 事件1
Element.addEventListener("click",function(){
    alert("1");
},boolean);

// 事件2
Element.addEventListener("click",function(){
    alert("2");
},boolean);

继续阅读 Javascript判断鼠标滚动方向

HTML5音乐播放器(五):同步显示歌词

好久没更新博客了,真是罪过,老偷懒,鄙视下自己,这两天把歌词同步显示的功能弄完了,先来说说原理:

1.ajax获取歌词文件(lrc文件)。

2.用正则分别去过滤出歌词和歌词前面对应的时间,存入数组。

[ti:很难]
[ar:张震岳]
[al:OK]
[by:]
[00:15.79]又一天的一个晴天
[00:19.07]阳光印在这张旧沙发
[00:22.96]躺下来 闭上眼
[00:25.11]恍然不知寂寞 枕着蔓延
.....
//获取歌词内容
lrcVal = xmlhttp.responseText.replace(/[dd:dd.dd]/g,"");
lrcArray = lrcVal.split("n");

//歌曲名
lrcArray[0].replace(/[ww:(.*?)]/g,function(){
    musicName = arguments[1] || "暂无";
});

//歌手
lrcArray[1].replace(/[ww:(.*?)]/g,function(){
    singer = arguments[1] || "暂无";
});

//获取歌词时间轴
xmlhttp.responseText.replace(/[(d*):(d*)([.|:]d*)]/g,function(){
    var min = arguments[1] | 0, //分
    sec = arguments[2] | 0, //秒
    realMin = min * 60 + sec; //计算总秒数

    lrcTimeArray.push(realMin);
});

继续阅读 HTML5音乐播放器(五):同步显示歌词

用Jquery.cookie判断用户是否第一次访问

挺长时间没更新博客了,最近事很多,公司的,家里的…还拖着HTML5的播放器的歌词功能没完成呢,惭愧啊,哈哈,近期回更新上:)

最近经常用到要判断用户是否第一次访问该网站,如果是的话,就出现一些帮助的 tips,现在的网站做的都越来越复杂了,这样对用户体验很有帮助。

这个功能如果你不想兴师动众,又没有用户登录的信息,对于“散户”,用前端来控制最简单的方式就是利用 cookie 了,但不足的就是清理cookie后,就又会判断为第一次访问了。如果想更精准的操作,那就操作数据库吧,但这又比较适合有用户登录记录的情况,对“散户”想精准,那就太难了。

首先引入 jQuery 和 jQuery.cookie,原理非常简单,给 cookie 设置一个 visits 值,通过这个值来判断用户是否是第一次访问本站。

提示:cookie 是需要服务环境支持的,本地不起作用。

    var options = {
        expires : 1
    };

    if ($.cookie('visits') == null) {
        //第一次访问
        $.cookie('visits', '1', options);
    }else{
        //不是第一次访问
    }

继续阅读 用Jquery.cookie判断用户是否第一次访问

HTML5音乐播放器(四):播放列表与播放方式

发现播放列表和播放方式切换两个功能是连在一起的,单独一个拿出来说不太合适,所以就都一块弄完了。废话不多说,进入主题,功能的逻辑我是这么设计的:

把整个歌曲文件的信息都写在 json 文件里,获取并生成播放列表。当播放歌曲时,系统会生成当前播放歌曲的引索值(例如:1,2,表示第二张专辑的第三首歌,这个很重要,歌曲切换都是基于这个引索值的),当要播放下一曲时,系统会根据当前是什么播放方式(列表循环,单曲循环,随机播放,顺序播放),生成新的引索值,然后通知播放器,下一首该怎么播放,是随机,还是循环,还是播放结束。

这部分代码不难,主要是要弄清楚整个流程应该是个什么方式,代码我就不贴了,demo 里都有,注释也都比较全。我描绘一下歌曲的播放流程,并写出它用到的函数,我想可能这种方法比较容易讲清楚:)

首先,我们要打开专辑列表,在打开的同时,生成专辑列表:

//---------------------------------------------------【功能:打开&关闭专辑列表】
MUSICENGINE.prototype.albumLists = function(){ ... };

//---------------------------------------------------【功能:生成专辑列表】
MUSICENGINE.prototype.formatAlbumLists = function(){ ... };

这时,可以播放了,我们可以双击专辑,打开专辑内的歌曲列表:

//---------------------------------------------------【双击专辑生成歌曲列表】
function formatInAlbumLists(song,AlbumName){ ... }

继续阅读 HTML5音乐播放器(四):播放列表与播放方式

HTML5音乐播放器(三):播放进度,时间显示以及音量的调节

时间真是不够用,只能晚上抽空把功能完善了,这节我们说下歌曲播放进度,时间显示以及音量的调节这三个部分。

一.歌曲播放进度

这个功能用到的 audio api 主要有:

currentTime() :以秒为单位返回从开始播放到目前所花的时间,也可设置 currentTime 的值来跳转到特定位置;

duration:获取媒体文件的播放时长,以秒为单位,如果无法获取,则为 NaN;

歌曲进度:首先先设置一个定时器,分别获取这两个值,用当前播放的秒数除以歌曲总秒数,再乘以歌曲进度条的总宽度(px),就能算出歌曲进度条一秒钟可以走多少了。

歌曲进度调节:点击进度条的某个位置,歌曲跳转到响应位置,这个功能的实现是先获取到点击的位置距离进度条最左边的长度(让 Firefox 支持 offsetX、offsetY),接着获得这段距离在整个进度条的长度里所占的百分比,用这个百分比乘以歌曲的总长度,就能算出该给 currentTime 设置的值了。

继续阅读 HTML5音乐播放器(三):播放进度,时间显示以及音量的调节