什么是window.chrome

在以前我们需要检测当前浏览器是否是chrome时,通常我们是使用UA进行检测的:

var isChrome = navigator.userAgent.toLowerCase().match(/chrome/) != null;
console.log( isChrome );

不过现在又有新的方法可以检测是否是chrome浏览器了。在chrome浏览器里有一个全局变量window.chrome,我们也直接使用这个全局变量来进行判断:

if( window.chrome ){
    alert('此浏览器为chrome浏览器');
}

在window.chrome的全局变量里,有appcsiloadTimesruntimewebstore这些属性和方法,不过网上关于window.chrome这个变量的资料还是很少。window.chrome.app和window.chrome.webstore可能跟chrome扩展程序和网上应用商店有关系。

1. window.chrome.csi

window.chrome.csi是一个方法,执行 window.chrome.csi() 后会获得3个属性(不同版本的chrome里也会有tran属性):

console.log( window.chrome.csi() );
{
    onloadT:1481031352403
    pageT:70382.456
    startE:1481031350604
    tran:15
}
  • startE : 页面开始加载时的毫秒时间戳(1481010621721);
  • onloadT : 页面加载完后的毫秒时间戳(1481010626707);
  • pageT : 自页面开始加载到现在的毫秒时间(5695686.301);
  • tran : 目前不知该字段是什么含义

因此onloadT减去startE就能获取到页面的加载时间。

2. window.chrome.loadTimes

window.chrome.loadTimes也是一个方法,执行后更多关于页面加载的信息:

console.log( window.chrome.loadTimes() );
{
    commitLoadTime:1481032100.719 
    connectionInfo:"http/1.1"
    finishDocumentLoadTime:1481032101.293
    finishLoadTime:1481032101.439
    firstPaintAfterLoadTime:0
    firstPaintTime:1481032101.013
    navigationType:"Reload"
    npnNegotiatedProtocol:"http/1.1"
    requestTime:1481032100.595
    startLoadTime:1481032100.595
    wasAlternateProtocolAvailable:false
    wasFetchedViaSpdy:false
    wasNpnNegotiated:true
}

目前这里面我也有几个字段的含义不太明白,还望各位网友指点:

跟时间有关的属性(单位全部为秒):

  1. requestTime : 请求时间;
  2. startLoadTime : 开始加载时间;
  3. commitLoadTime
  4. firstPaintTime : 首次渲染时间
  5. firstPaintAfterLoadTime
  6. finishDocumentLoadTime : 文档首次加载完成时间
  7. finishLoadTime : 页面加载完成时间

其他属性:

  • connectionInfo : 连接协议(主要有: http/1.1,h2, quic/1+spdy/3等)
  • navigationType : 打开类型(Reload:刷新页面, Other:首次打开页面)
  • npnNegotiatedProtocol : 不明白含义,与connectionInfo保持一致
  • wasFetchedViaSpdy : 是否通过spdy协议传输

除了通过开发者工具查看加载时间外,也可以通过js直接打印各个时间。

3. 总结

在上面讲述的两个属性里,有两个属性表示的含义是相同的,不过只是时间单位不同而已:

var csi = window.chrome.csi(),
    loadTimes = window.chrome.loadTimes();

csi.startE == loadTimes.startLoadTime*1000;
csi.onloadT == loadTimes.finishDocumentLoadTime*1000;

在多次执行window.chrome.csi()window.chrome.loadTimes()后发现,除了pageT属性是表示当前页面打开的时间外,其他的属性都不会变的。因此只要这两个方法在页面加载完成后执行,不论什么时候执行,都是能获取到准确的数据的。

通过这个特性,我们就可以写一个方法来打印我们想要的时间:

window.onload = function(){
    if( window.chrome ){
        var loadtime = window.chrome.loadTimes();

        console.log('开始加载时间: '+loadtime.startLoadTime);
        console.log('文档加载完成时间: '+loadtime.finishDocumentLoadTime);
        console.log('页面加载完成时间: '+loadtime.finishLoadTime);
        // ...
    }
}

欢迎各位提出意见或建议。

innerHTML对IScroll组件的影响

我们在移动端的页面中有时候会使用到IScroll的滚动组件,要么是应用到页面的整个区域,要么是其中的某个div。当我们已经对这块区域进行初始化后,再向页面中添加元素时会出现什么情况呢?

1. 向滚动区域中添加元素

一种情况是向滚动区域中添加元素,这个元素影响到了滚动内容的高度,比如这样的代码,.main是滚动区域的外壳:

var myscroll = new IScroll('.main', {
    mouseWheel: true,
    scrollbars: true
});

document.querySelector('.main .con').innerHTML += '
author: wenzi
';

在向页面中添加一个div元素后,如果不做任何改动,滚动条拉到底部是看不到这个新div元素的。只有使用refresh()方法,刷新滚动区域后,滚动条才重新计算:

myscroll.refresh();

2. 向滚动区域的父级元素中添加元素

还有一种情况就是向滚动区域的父级元素中添加元素,比如直接追加到元素body中:

document.querySelector('.main .con').innerHTML += '
author: wenzi
';

当我们以innerHTML方式向body中追加元素后,出现了一个严重的问题,IScroll滚动区域直接卡死,无法滚动了。即使使用refresh()方法也不能奏效。

那么是什么原因造成的呢?问题就出在innerHTML上,使用innerHTML为该元素添加html代码时,实际上是重写了该元素里所有的html代码,之前的代码全部被覆盖掉,才导致绑定在内部的IScroll事件失效,无法进行滚动了。

在第一部分中,因为修改的是在IScroll滚动区域内部的内容,不会影响到IScroll的执行,所以添加的内容只是影响了滚动区域的高度,但滚动功能还是能正常使用的。

我们可以再做一个实验: 使用innerHTML.main元素的兄弟元素里添加或者修改元素,IScroll是完全没有影响的。

3. 解决方案

那使用什么方法向某个元素(比如body)中追加元素呢,答案是appendChild了,我们可以先用createElement创建一个div元素,然后把内部所有的内容都填充到这个div的内容,然后再在该元素上appendChild这个div元素:

var div = document.createElement('div');
div.className = 'dialog';
div.innerHTML = '
author: wenzi
'; document.querySelector('body').appendChild(div);

这种方式既不会对IScroll造成影响,也不用对IScroll的代码进行改动。方便作为第三方向某个成熟的系统中添加代码,不会对原系统产生影响。

4. 总结

关于innerHTML和appendChild,这里稍微总结下:

innerHTMLappendChild
功能重写该元素内所有的html向该元素追加一个子元素
性能
事件需添加到页面上才能绑定事件创建后即可绑定事件
耦合度

关于使用哪种方式添加元素还得看这个项目的复杂程度。如果仅仅要实现一个简单的需求的话,用innerHTML性能还是好些。如果需要大规模重写html的话,可以使用模板引擎来完成。

单页面应用中js获取url中的参数

基于Vue开发的单页面应用,单页面应用里是使用#hash进行路由切换的,那么现在的问题是: 无论之前的URL有没有带参数,都会在最后加上#

比如从其他链接跳转过来时带有参数,页面的URL就会变成这样:
/?a=1&b=2#/

如果从主页再往二级页跳转时还带有参数,页面的URL就会成这样:
/?a=1&b=2#/user?uname=蚊子&year=2018

在这个具有两个?的链接,location.search获取到的是哪个值?测试一下我们就会发现,获取到的是前面?#之前的参数:

console.log( location.search );
// "?a=1&b=2"

因此这里我们需要对整个url进行检索,这样hash#前后的数据就都能获取到了:

function getQueryString(name) {
    var reg = new RegExp("[?&]" + name + "=([^&#]*)", "i");
    var res = window.location.href.match(reg);

    if( res && res.length>1 ){
        return decodeURIComponent(res[1]);
    }
    return '';
}

getQueryString('a'); // "1"
getQueryString('b'); // "2"
getQueryString('uname'); // "蚊子"
getQueryString('year'); // "2018"

到这里这个功能已经完工了。不过在我们项目的具体实施中,还有一个特殊的需求: 有的参数会以json string格式放在某个字段里传递进来,然后页面再获取到这个字段后进行JSON.parse转换,最后才能获取到json中的值。之前是每次getQueryString('extra')获取到数据后,再进行json解析,而且为了防止json解析错误,还都要加上try...catch

因此,我也把这个特殊的需求融入到了这个函数里,过程也非常简单,就是多了一步额外的检测:

// name: 要获取的字段
// extra json string存放的字段
function getQueryString(name, extra='extra') {
    var get = function(_name){
        var reg = new RegExp("[?&]" + _name + "=([^&#]*)", "i");
        var res = window.location.href.match(reg);

        if( res && res.length>1 ){
            return decodeURIComponent(res[1]);
        }
        return '';
    }

    var res = get(name);

    // 若存在参数则直接返回
    if( res ){
        return res;
    }

    // 若不存在,则检测需要的参数是否包含在extra中
    let _addparams = get(extra);
    if( _addparams ){
        try{
            let info = JSON.parse(_addparams);
            return info[name];
        }catch(e){

        }
    }
    return '';
}

这样在项目的其他地方直接使用即可,不用再考虑name字段具体在哪个位置。

border-radius将div或者button变成圆形

利用border-radius将标签设置成圆形,只要将border-radius的值设置成50%。

具体请看以下代码:

<html>
    <head>
    </head>
    <body style="width: 99%;height: 1000px;">
        <div style="width:100px;height:100px;background-color:#000000;border-radius:50%;"></div>
        <input type="button" style="width: 100px;height: 100px;background-color: #000000;border-radius:50%;" />
    </body>
</html>

CSS3 box-sizing 属性

box-sizing 属性允许您以特定的方式定义匹配某个区域的特定元素。

例如,假如您需要并排放置两个带边框的框,可通过将 box-sizing 设置为 “border-box”。这可令浏览器呈现出带有指定宽度和高度的框,并把边框和内边距放入框中。

语法

box-sizing: content-box|border-box|inherit;

实例

div{
    box-sizing:border-box;
    -moz-box-sizing:border-box; /* Firefox */
    -webkit-box-sizing:border-box; /* Safari */
    width:50%;
    float:left;
}