分类目录归档:前端

移动端点击事件触发顺序

移动端页面事件触发顺序和事件触发规则不同于PC端,一般都知道click在移动端会延迟200~300ms触发。
本文从实例出发,演示移动端事件的触发规则和顺序。

HTML页面:

<body>
<script type="text/javascript">
var firstEmitTime = 0;
//页面输出日志
function showLog(msg){
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
}
</script>

<a href="javascript:
    var aa='href: ' + ((new Date()).getTime() - firstEmitTime);
    showLog(aa);console.log(aa);"
    id="aa">This is a href</a>
<script type="text/javascript" src="tmp.js"></script>
</body>

tmp.js文件:

var aa = document.getElementById("aa");
var eventTypeArr = ['click', 'touchstart', 'touchmove', 'touchend', 
        'mousedown', 'mousemove', 'mouseover', 'mouseup'];

for(var i = 0; i < eventTypeArr.length; i++){
    //利用闭包保存eventType,当回调函数触发时会访问该闭包的环境变量对象
    (function(){
        var eventType = eventTypeArr[i];
        aa.addEventListener(eventType, function(){
            var curTime = (new Date()).getTime();
            if(firstEmitTime === 0){
                firstEmitTime = curTime;
            }
            //打印当前事件触发时间与第一个事件触发时间的差值
            var log = eventType + ': ' + (curTime - firstEmitTime);
            console.log(log);
            showLog(log);
        });
    })();
}

iPhone4/iOS7.0.4上如果点击(非长按),则输出结果大多为(事件触发顺序不变,间隔时间略有不同):
touchstart:0
touchend:204
mouseover:243
mousemove:243
果长按链接,经一百多次测试,下面是两种输出结果,约70%是第一种情况(由于受点击时长和力度的影响,此数据仅供参考):
第一种:
touchstart:0
mouseover:2238
mousedown:2239
mouseup:2243
click:2244
href:2245

第二种:
mouseover:0
mousedown:0
mouseup:5
click:5
href:5
其中,href内为JavaScript代码时,浏览器会提示是否打开,需要点击打开,才会输出mouseover及其后面的值,因此与touchstart的间隔可能不是200~300ms。
从结果可以看出该环境下:
1、与click触发时,touchstart未必触发;
2、mouse系列事件与click事件是相近时间内触发,大约在touchstart后延迟200~300ms后触发。

华为MT1-U06/Android4.1.2上点击链接(非长按),测试一百多次,主要有下面三种结果,出现概率分别为70%、25%和5%(由于受点击时长和力度的影响,此数据仅供参考):
第一种:
mouseover:0
mousemove:6
touchstart:12
touchend:568
mousedown:869
mouseup:874
click:876
href:879

第二种:
mouseover:0
mousemove:7
mousedown:387
mouseup:392
click:394
href:396

第三种:
mouseover:0
mousemove:8
touchstart:12
touchmove:91
touchend:95
可以看出该环境下:
1、mouse事件会先于touch事件开始,而迟于其结束。
2、click和href事件触发仍是最后触发,会延迟300ms左右。

页面引入JavaScript脚本

1、直接添加代码块
通过script标签,可直接将JavaScript代码嵌入网页
Example:

<script type="text/javascript">
  document.write("Hello World!")
</script>

2、加载外部脚本

<script charset="utf-8" src="example.js"></script>

直接将script标签写在页面上,浏览器加载时会阻塞页面渲染,知道js加载完成。

异步加载的脚本可以这样引入

<script src="js/require.js" defer async="async"></script>

其中,defer规定是否对脚本执行进行延迟,直到页面加载为止。但只有Internet Explorer支持defer属性。
async属性可以保证脚本下载的同时,浏览器继续渲染。一旦渲染完成,再执行脚本文件,这就是“非同步执行”的意思。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。IE 10支持async属性,低于这个版本的IE都不支持。如果同时使用async和defer属性,后者不起作用,浏览器行为由async属性决定。

如果 async=”async”:脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)
如果不使用 async 且 defer=”defer”:脚本将在页面完成解析时执行
如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本

3、动态嵌入脚本
创建script元素,插入页面,可动态嵌入脚本,如下:

var jsfile = ['/js/logic/ee.js', '/js/logic/ff.js'];
for(var i = 0; i < jsfile.length; i++){
    var script = document.createElement('script');
    script.src = jsfile[i];
    script.async = false;
    var src = jsfile[i];
    script.onload = function(){
        console.log('onload ' + this.src);
    }
    document.head.appendChild(script);
}

不管async是不是设置为false,都不会阻塞页面渲染。但设置async为false,可保证按照添加到页面的顺序加载js,设置为true或不设置都不会按顺序加载。
还有就是,js加载下来后会立即执行,然后才会执行onload方法。

参考:
http://javascript.ruanyifeng.com/bom/engine.html

JavaScript数组方法map

map方法是我最喜欢的方法了,虽然简单,但功能强大,用起来非常方便。在ECMPAScript 5中也添加了这个方法。

map()方法功能:将调用的数组的每个元素传递给指定的函数,并返回一个数组,该数组由函数的返回值构成。

注:map返回的是新数组,它不修改调用的数组。

为了兼容不支持map的浏览器,developer.mozilla.org上给出了map兼容性解决方法。

// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
  Array.prototype.map = function(callback, thisArg) {

    var T, A, k;

    if (this == null) {
      throw new TypeError(" this is null or not defined");
    }

    // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If IsCallable(callback) is false, throw a TypeError exception.
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== "function") {
      throw new TypeError(callback + " is not a function");
    }

    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
    if (thisArg) {
      T = thisArg;
    }

    // 6. Let A be a new array created as if by the expression new Array(len) where Array is
    // the standard built-in constructor with that name and len is the value of len.
    A = new Array(len);

    // 7. Let k be 0
    k = 0;

    // 8. Repeat, while k < len
    while(k < len) {

      var kValue, mappedValue;

      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
        kValue = O[ k ];

        // ii. Let mappedValue be the result of calling the Call internal method of callback
        // with T as the this value and argument list containing kValue, k, and O.
        mappedValue = callback.call(T, kValue, k, O);

        // iii. Call the DefineOwnProperty internal method of A with arguments
        // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
        // and false.

        // In browsers that support Object.defineProperty, use the following:
        // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });

        // For best browser support, use the following:
        A[ k ] = mappedValue;
      }
      // d. Increase k by 1.
      k++;
    }

    // 9. return A
    return A;
  };      
}