分享
是一种美德

JavaScript 的性能优化:加载和执行

wangtong阅读(99)

随着 Web2.0 技术的不断推广,越来越多的应用使用 JavaScript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题。而这个问题又因 JavaScript 的阻塞特性变的复杂,也就是说当浏览器在执行 JavaScript 代码时,不能同时做其他任何事情。本文详细介绍了如何正确的加载和执行 JavaScript 代码,从而提高其在浏览器中的性能。
概览
无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时出现阻塞的原因在于,脚本可能会改变页面或 JavaScript 的命名空间,它们对后面页面内容造成影响。一个典型的例子就是在页面中使用document.write()。例如清单 1
清单 1 JavaScript 代码内嵌示例

<html>
<head>
    <title>Source Example</title>
</head>
<body>
    <p>
    <script type="text/javascript">
        document.write("Today is " + (new Date()).toDateString());
    </script>
    </p>
</body>
</html>

当浏览器遇到script标签时,当前 HTML 页面无从获知 JavaScript 是否会向p 标签添加内容,或引入其他元素,或甚至移除该标签。因此,这时浏览器会停止处理页面,先执行 JavaScript代码,然后再继续解析和渲染页面。同样的情况也发生在使用 src 属性加载 JavaScript的过程中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它。在这个过程中,页面渲染和用户交互完全被阻塞了。

脚本位置
HTML 4 规范指出 script 标签可以放在 HTML 文档的head或body中,并允许出现多次。Web 开发人员一般习惯在 head 中加载外链的 JavaScript,接着用 link 标签用来加载外链的 CSS 文件或者其他页面信息。例如清单 2
清单 2 低效率脚本位置示例

<html>
<head>
    <title>Source Example</title>
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
    <script type="text/javascript" src="script3.js"></script>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Hello world!</p>
</body>
</html>

然而这种常规的做法却隐藏着严重的性能问题。在清单 2 的示例中,当浏览器解析到 script标签(第 4 行)时,浏览器会停止解析其后的内容,而优先下载脚本文件,并执行其中的代码,这意味着,其后的 styles.css 样式文件和body标签都无法被加载,由于body标签无法被加载,那么页面自然就无法渲染了。因此在该 JavaScript 代码完全执行完之前,页面都是一片空白。图 1 描述了页面加载过程中脚本和样式文件的下载过程。
图 1 JavaScript 文件的加载和执行阻塞其他文件的下载

我们可以发现一个有趣的现象:第一个 JavaScript 文件开始下载,与此同时阻塞了页面其他文件的下载。此外,从 script1.js 下载完成到 script2.js 开始下载前存在一个延时,这段时间正好是 script1.js 文件的执行过程。每个文件必须等到前一个文件下载并执行完成才会开始下载。在这些文件逐个下载过程中,用户看到的是一片空白的页面。
从 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 开始都允许并行下载 JavaScript 文件。这是个好消息,因为script标签在下载外部资源时不会阻塞其他script标签。遗憾的是,JavaScript 下载过程仍然会阻塞其他资源的下载,比如样式文件和图片。尽管脚本的下载过程不会互相影响,但页面仍然必须等待所有 JavaScript 代码下载并执行完成才能继续。因此,尽管最新的浏览器通过允许并行下载提高了性能,但问题尚未完全解决,脚本阻塞仍然是一个问题。
由于脚本会阻塞页面其他资源的下载,因此推荐将所有script标签尽可能放到body标签的底部,以尽量减少对整个页面下载的影响。例如清单 3
清单 3 推荐的代码放置位置示例

<html>
<head>
    <title>Source Example</title>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Hello world!</p>
  
    <!-- Example of efficient script positioning -->
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
    <script type="text/javascript" src="script3.js"></script>
</body>
</html>

这段代码展示了在 HTML 文档中放置script标签的推荐位置。尽管脚本下载会阻塞另一个脚本,但是页面的大部分内容都已经下载完成并显示给了用户,因此页面下载不会显得太慢。这是优化 JavaScript 的首要规则:将脚本放在底部。

组织脚本
由于每个script标签初始下载时都会阻塞页面渲染,所以减少页面包含的script标签数量有助于改善这一情况。这不仅针对外链脚本,内嵌脚本的数量同样也要限制。浏览器在解析 HTML 页面的过程中每遇到一个script标签,都会因执行脚本而导致一定的延时,因此最小化延迟时间将会明显改善页面的总体性能。
这个问题在处理外链 JavaScript 文件时略有不同。考虑到 HTTP 请求会带来额外的性能开销,因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也就是说,减少页面中外链脚本的数量将会改善性能。
通常一个大型网站或应用需要依赖数个 JavaScript 文件。您可以把多个文件合并成一个,这样只需要引用一个script标签,就可以减少性能消耗。文件合并的工作可通过离线的打包工具或者一些实时的在线服务来实现。
需要特别提醒的是,把一段内嵌脚本放在引用外链样式表的link之后会导致页面阻塞去等待样式表的下载。这样做是为了确保内嵌脚本在执行时能获得最精确的样式信息。因此,建议不要把内嵌脚本紧跟在link标签后面。

无阻塞的脚本
减少 JavaScript 文件大小并限制 HTTP 请求数在功能丰富的 Web 应用或大型网站上并不总是可行。Web 应用的功能越丰富,所需要的 JavaScript 代码就越多,尽管下载单个较大的 JavaScript 文件只产生一次 HTTP 请求,却会锁死浏览器的一大段时间。为避免这种情况,需要通过一些特定的技术向页面中逐步加载 JavaScript 文件,这样做在某种程度上来说不会阻塞浏览器。
无阻塞脚本的秘诀在于,在页面加载完成后才加载 JavaScript 代码。这就意味着在 window 对象的 onload事件触发后再下载脚本。有多种方式可以实现这一效果。
延迟加载脚本
HTML 4 为script标签定义了一个扩展属性:defer。Defer 属性指明本元素所含的脚本不会修改 DOM,因此代码能安全地延迟执行。defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。在其他浏览器中,defer 属性会被直接忽略,因此script标签会以默认的方式处理,也就是说会造成阻塞。然而,如果您的目标浏览器支持的话,这仍然是个有用的解决方案。清单 4 是一个例子
清单 4 defer 属性使用方法示例

<script type="text/javascript" src="script1.js" defer></script>

带有 defer 属性的script标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到script标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。
任何带有 defer 属性的script元素在 DOM 完成加载之前都不会被执行,无论内嵌或者是外链脚本都是如此。清单 5 的例子展示了defer属性如何影响脚本行为:
清单 5 defer 属性对脚本行为的影响

<html>
<head>
    <title>Script Defer Example</title>
</head>
<body>
    <script type="text/javascript" defer>
        alert("defer");
    </script>
    <script type="text/javascript">
        alert("script");
    </script>
    <script type="text/javascript">
        window.onload = function(){
            alert("load");
        };
    </script>
</body>
</html>

这段代码在页面处理过程中弹出三次对话框。不支持 defer 属性的浏览器的弹出顺序是:“defer”、“script”、“load”。而在支持 defer 属性的浏览器上,弹出的顺序则是:“script”、“defer”、“load”。请注意,带有 defer 属性的script元素不是跟在第二个后面执行,而是在 onload 事件被触发前被调用。
如果您的目标浏览器只包括 Internet Explorer 和 Firefox 3.5,那么 defer 脚本确实有用。如果您需要支持跨领域的多种浏览器,那么还有更一致的实现方式。
HTML 5 为script标签定义了一个新的扩展属性:async。它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。
动态脚本元素
文档对象模型(DOM)允许您使用 JavaScript 动态创建 HTML 的几乎全部文档内容。script元素与页面其他元素一样,可以非常容易地通过标准 DOM 函数创建:
清单 6 通过标准 DOM 函数创建script元素

var script = document.createElement ("script");
   script.type = "text/javascript";
   script.src = "script1.js";
   document.getElementsByTagName("head")[0].appendChild(script);

新的script元素加载 script1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。您甚至可以将这些代码放在head部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。
当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是“自运行”类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情况下,您需要跟踪脚本下载完成并是否准备妥善。可以使用动态 script 节点发出事件得到相关信息。
Firefox、Opera, Chorme 和 Safari 3+会在script节点接收完成之后发出一个 onload 事件。您可以监听这一事件,以得到脚本准备好的通知:
清单 7 通过监听 onload 事件加载 JavaScript 脚本

var script = document.createElement ("script")
script.type = "text/javascript";
  
//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
    alert("Script loaded!");
};
  
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);

Internet Explorer 支持另一种实现方式,它发出一个 readystatechange 事件。script元素有一个 readyState 属性,它的值随着下载外部文件的过程而改变。readyState 有五种取值:
“uninitialized”:默认状态
“loading”:下载开始
“loaded”:下载完成
“interactive”:下载完成但尚不可用
“complete”:所有数据已经准备好

微软文档上说,在script元素的生命周期中,readyState 的这些取值不一定全部出现,但并没有指出哪些取值总会被用到。实践中,我们最感兴趣的是“loaded”和“complete”状态。Internet Explorer 对这两个 readyState 值所表示的最终状态并不一致,有时script元素会得到“loader”却从不出现“complete”,但另外一些情况下出现“complete”而用不到“loaded”。最安全的办法就是在 readystatechange 事件中检查这两种状态,并且当其中一种状态出现时,删除 readystatechange 事件句柄(保证事件不会被处理两次):
清单 8 通过检查 readyState 状态加载 JavaScript 脚本

var script = document.createElement("script")
script.type = "text/javascript";
  
//Internet Explorer
script.onreadystatechange = function(){
     if (script.readyState == "loaded" || script.readyState == "complete"){
           script.onreadystatechange = null;
           alert("Script loaded.");
     }
};
  
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);

大多数情况下,您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:
清单 9 通过函数进行封装

function loadScript(url, callback){
    var script = document.createElement ("script")
    script.type = "text/javascript";
    if (script.readyState){ //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" || script.readyState == "complete"){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //Others
        script.onload = function(){
            callback();
        };
    }
    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

此函数接收两个参数:JavaScript 文件的 URL,和一个当 JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步,设置 src 属性,并将script元素添加至页面。此 loadScript() 函数使用方法如下:
清单 10 loadScript()函数使用方法

loadScript("script1.js", function(){
    alert("File is loaded!");
});

您可以在页面中动态加载很多 JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以保证他们的次序,如下:
清单 11 通过 loadScript()函数加载多个 JavaScript 脚本

loadScript("script1.js", function(){
    loadScript("script2.js", function(){
        loadScript("script3.js", function(){
            alert("All files are loaded!");
        });
    });
});

此代码等待 script1.js 可用之后才开始加载 script2.js,等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。
[size=1.166em]动态脚本加载是非阻塞 JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。
使用 XMLHttpRequest(XHR)对象
此技术首先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态 script 元素将 JavaScript 代码注入页面。清单 12 是一个简单的例子:
清单 12 通过 XHR 对象加载 JavaScript 脚本

var xhr = new XMLHttpRequest();
xhr.open("get", "script1.js", true);
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
            var script = document.createElement ("script");
            script.type = "text/javascript";
            script.text = xhr.responseText;
            document.body.appendChild(script);
        }
    }
};
xhr.send(null);

此代码向服务器发送一个获取 script1.js 文件的 GET 请求。onreadystatechange 事件处理函数检查 readyState 是不是 4,然后检查 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的script元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的script元素。一旦新script元素被添加到文档,代码将被执行,并准备使用。
这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在script标签之外(换句话说不受script标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。
[size=1.166em]此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指”内容投递网络(Content Delivery Network)”,所以大型网页通常不采用 XHR 脚本注入技术。
总结
减少 JavaScript 对性能的影响有以下几种方法:
将所有的script标签放到页面底部,也就是闭合标签之前,这能确保在脚本执行前页面已经完成了渲染。
尽可能地合并脚本。页面中的script标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
采用无阻塞下载 JavaScript 脚本的方法:

使用script标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);
使用动态创建的script元素来下载并执行代码;
使用 XHR 对象下载 JavaScript 代码并注入页面中。

通过以上策略,可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 网站和应用的实际性能。

根据验证邮箱的域名跳转到相应的登录页面

wangtong阅读(208)

之前由于公司项目要求,需要根据邮箱跳转到对应的邮箱,百度了半天才找到一个,不多说直接帖代码

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>js邮箱地址跳转</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
var hash={
'qq.com': 'http://mail.qq.com',
'gmail.com': 'http://mail.google.com',
'sina.com': 'http://mail.sina.com.cn',
'163.com': 'http://mail.163.com',
'126.com': 'http://mail.126.com',
'yeah.net': 'http://www.yeah.net/',
'sohu.com': 'http://mail.sohu.com/',
'tom.com': 'http://mail.tom.com/',
'sogou.com': 'http://mail.sogou.com/',
'139.com': 'http://mail.10086.cn/',
'hotmail.com': 'http://www.hotmail.com',
'live.com': 'http://login.live.com/',
'live.cn': 'http://login.live.cn/',
'live.com.cn': 'http://login.live.com.cn',
'189.com': 'http://webmail16.189.cn/webmail/',
'yahoo.com.cn': 'http://mail.cn.yahoo.com/',
'yahoo.cn': 'http://mail.cn.yahoo.com/',
'eyou.com': 'http://www.eyou.com/',
'21cn.com': 'http://mail.21cn.com/',
'188.com': 'http://www.188.com/',
'foxmail.coom': 'http://www.foxmail.com'
};
  
$(function(){
$(".mail").each(function() {
var url = $(this).text().split('@')[1];
for (var j in hash){
$(this).attr("href", hash[url]);
}
});
})
</script>
</head>
  
<body>
<a href="#" target="_blank" class="mail"><a href="mailto:laike@qq.com">laike@qq.com</a></a> <br>
<a href="#" target="_blank" class="mail"><a href="mailto:laike@gmail.com">laike@gmail.com</a></a> <br>
<a href="#" target="_blank" class="mail"><a href="mailto:laike@sina.com">laike@sina.com</a></a> <br>
<a href="#" target="_blank" class="mail"><a href="mailto:laike@163.com">laike@163.com</a></a> <br>
<a href="#" target="_blank" class="mail"><a href="mailto:laike@souhu.com">laike@souhu.com</a></a> <br>
<a href="#" target="_blank" class="mail"><a href="mailto:laike@hotmail.com">laike@hotmail.com</a></a>
</body>
</html>

之后,有大神帮忙优化调整了一下,还是那句话,直接上代码

<input type="text" class="txt" />
<a href="javascript:" class="submit">立即前往邮箱</a>
<script src="http://libs.useso.com/js/jquery/1.10.0/jquery.min.js"></script>
<script>
var hash = {
        'qq.com': 'http://mail.qq.com',
        'gmail.com': 'http://mail.google.com',
        'sina.com': 'http://mail.sina.com.cn',
        '163.com': 'http://mail.163.com',
        '126.com': 'http://mail.126.com',
        'yeah.net': 'http://www.yeah.net/',
        'sohu.com': 'http://mail.sohu.com/',
        'tom.com': 'http://mail.tom.com/',
        'sogou.com': 'http://mail.sogou.com/',
        '139.com': 'http://mail.10086.cn/',
        'hotmail.com': 'http://www.hotmail.com',
        'live.com': 'http://login.live.com/',
        'live.cn': 'http://login.live.cn/',
        'live.com.cn': 'http://login.live.com.cn',
        '189.com': 'http://webmail16.189.cn/webmail/',
        'yahoo.com.cn': 'http://mail.cn.yahoo.com/',
        'yahoo.cn': 'http://mail.cn.yahoo.com/',
        'eyou.com': 'http://www.eyou.com/',
        '21cn.com': 'http://mail.21cn.com/',
        '188.com': 'http://www.188.com/',
        'foxmail.coom': 'http://www.foxmail.com'
};
$('.txt').on('blur',function (){
        var url = $(this).val().split('@')[1];
        for (var j in hash) {
                $('.submit').attr("href", hash[url]);
        }
});
 
</script>

关于微信自定义分享的一点心得

wangtong阅读(120)

自从微信2017年4月份把分享接口改了之后,之前那种在页面选择第一张图片大于300px像素作为分享的默认图的方法已经失效,必须经过自定义接口来指定微信分享的图片。前几天做了一个项目,客户要求网站分享,必须要带自家的logo图片。于是查找官方api。总结一下步骤,以及步骤中可能遇到的坑。

1.在分享过程中,有个前提条件,由于调用微信的分享接口必须和微信的公众号进行绑定,也就是登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。此处有个坑,就是你的公众号,必须经过微信的官方认证,好像是300rmb左右,具体我也记不清,只有经过认证之后,在开发者权限里面里面,才能调用自定义接口。这就是所谓的大厂。

2.引入官方的微信js.http://res.wx.qq.com/open/js/jweixin-1.2.0.js 跟我们平常引入的js是一样的。

3.通过ajax,调用后台接口(最好是json文件),来配置wx.config文件,

var url = encodeURI(location.href.split('#')[0]);  //域名必须要去掉#号。
$.ajax({  
type : "post",  
dataType:"json",
data:{'url':url},
url:"".//你的后台接口地址,建议用json格式。
    success:function(data){    //此处有坑,success后面的空格要去掉。不然后面莫名其妙的走不通。
        console.log(data);
        wx.config({  
            debug: false, //调试的时候,开启,正式上线关闭,不然会在移动端弹出信息。
            appId:data.appId,  // 公众号的appid、
            timestamp:data.timestamp,  //时间戳 动态的。
            nonceStr:data.nonceStr,   //生成签名的随机串
            signature:data.signature,   // 必填,签名,
            jsApiList: [      //接口数组,这儿写几个,底下就写几个。
            "onMenuShareTimeline", //分享给好友  
            "onMenuShareAppMessage", //分享到朋友圈  
            "onMenuShareQQ",  //分享到QQ  
            ]  
        });  
        console.log("链接成功");
        wx.ready(function(){  
            var shareData = {  
                title: '{{title}}' //标题,  
                desc: '{{title}}',//这里请特别注意是要去除html  
                link: url, 
                imgUrl: "",//图片的自定义地址。
            };  
            wx.onMenuShareTimeline(shareData);  
            wx.onMenuShareAppMessage(shareData);  
            wx.onMenuShareQQ(shareData);  
    });      
    },  
    error:function(data){  
    console.log("返回失败");
    }  
});

其余的微信的api已经讲解的很详细了。只是指出来大家少踩坑。这就是一个完整的调用微信分享的接口。上面的坑已经指出。

30行js让你的rem弹性布局适配所有分辨率(含竖屏适配)

wangtong阅读(89)

用rem来实现移动端的弹性布局是个好主意!用法如下:
CSS

@media only screen and (max-width: 320px), only screen and (max-device-width:320px) {    html {      font-size:10px;
    }}@media only screen and (max-width: 640px), only screen and (max-device-width:640px) {    html {      font-size:20px;
    }}.test-div{width: 10rem;}

那么这个.test-div的宽度在320px的分辨率下会是10 10 = 100px, 在640下是10 20 = 200px,从而达到了弹性缩放的目的。
但是这样做还是有2个问题:

  1. 随着各种新手机的发布,分辨率也碎片化了,我们无法预知将来会出现的分辨率宽度,我们不可能把所有要兼容的分辨率写到css里。
  2. 这样写只能做到页面适配不同的宽度,对于那种在各种屏幕上都要在一屏幕内显示的页面,就没有办法适配了。
    比如这种非常流行的整屏滑动页面,当屏幕宽高比小于设计稿的比例时会缩放:

所以完美解决适配的问题就得靠js了,思路非常简单,判断一下当前终端的宽度(这里在安卓上有个坑,后面会说)和设计稿宽度的比例,计算出需要缩放的倍数,然后根据这个倍数值改变html的字体大小即可。
如果需要横竖屏都适配,那么根据终端宽高比例较小的那一个来计算。用通俗的语言来说,如果终端屏幕比设计稿更加宽矮一些,那么久根据它和设计稿的高度比例来计算字体。
思路永远是简单,实现永远是有问题需要解决的,先上代码:
https://github.com/twang211/webdemo/tree/master/setHtmlRem
JAVASCRIPT

<font style="color:rgb(51, 51, 51)">console.time("test");/*
    # 按照宽高比例设定html字体, width=device-width initial-scale=1版
    # @pargam win 窗口window对象
    # @pargam option{
      designWidth: 设计稿宽度,必须
      designHeight: 设计稿高度,不传的话则比例按照宽度来计算,可选
      designFontSize: 设计稿宽高下用于计算的字体大小,默认20,可选
      callback: 字体计算之后的回调函数,可选
    }
    # return Boolean;
    # <a href="mailto:twang211@gmail.com">twang211@gmail.com</a>
    # ps:请尽量第一时间运行此js计算字体
*/!function(win, option) {  var count = 0, 
      designWidth = option.designWidth, 
      designHeight = option.designHeight || 0, 
      designFontSize = option.designFontSize || 20, 
      callback = option.callback || null,
      root = document.documentElement,
      body = document.body,
      rootWidth, newSize, t, self;
      root.style.width = 100%;  //返回root元素字体计算结果
  function _getNewFontSize() {
    var scale = designHeight !== 0 ? Math.min(win.innerWidth / designWidth, win.innerHeight / designHeight) : win.innerWidth / designWidth;    return parseInt( scale * 10000 * designFontSize ) / 10000;
  }
  !function () {
    rootWidth = root.getBoundingClientRect().width;    self = self ? self : arguments.callee;    //如果此时屏幕宽度不准确,就尝试再次获取分辨率,只尝试20次,否则使用win.innerWidth计算
    if( rootWidth !== win.innerWidth &&  count < 20 ) {
      win.setTimeout(function () {
        count++;        self();
      }, 0);
    } else {
      newSize = _getNewFontSize();      //如果css已经兼容当前分辨率就不管了
      if( newSize + 'px' !== getComputedStyle(root)['font-size'] ) {
        root.style.fontSize = newSize + "px";        return callback && callback(newSize);
      };
    };
  }();  //横竖屏切换的时候改变fontSize,根据需要选择使用
  win.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
    clearTimeout(t);
    t = setTimeout(function () {
      self();
    }, 300);
  }, false);
}(window, {
  designWidth: 640, 
  designHeight: 1136,
  designFontSize: 20,
  callback: function (argument) {
    console.timeEnd("test")
  }
});</font>

然后再说几个点和问题:1. 这段代码对viewport有要求,必须是width=device-width initial-scale=1,即窗口的大小是设备物理宽度(分辨率 / devicePixelRatio),并且禁止缩放。另外还有一种做法就是手机淘宝的做法,窗口大小是分辨率宽度,然后缩放倍数是1/devicePixelRatio,这里暂且不讨论。
2.就是解决安卓上的问题。经过实测,有些安卓机器,使用1的viewport,在页面刚加载的时候。不管是读取window.innerWidth,还是doc的getBoundingClientRect().width,或者是body的clientWidth,都不是设备的物理宽度。所以只好祭出黑魔法setTimeout,一试果然可以,异步100ms执行获取屏幕宽度的代码就准确了。但是这种不可控的代码让人不爽。
因为width=device-width initial-scale=1,documentElement的宽度又是100%,所以当这两个值相等的时候我们可以认为目前获取到的屏幕宽度是准确的。那么使用此条件作为判断条件,不断的setTimeout(fun(){}, 0)去判断,当此条件为真时改变documentElement的字体。可以尽可能快的执行目标代码。但是又万一这两个值一直不相等又不能无限的死循环下去,所以设置了一个尝试上限,到上限之后用窗口宽度来计算(缩放比例不对的话用户起码可以看到完整的页面)。在chrome下测试,执行40次代码的平均时间是230ms,考虑到安卓机的js引擎速度,将上限设为了20。
3.是执行时机,个人建议将这段代码放到head里,第一时间计算好html的fontSize,避免重绘。如果你有有一些跟获取dom元素尺寸相关的操作,就得放到这个计算函数的回调里面了,这时候就不能放到head里(因为运行的时候dom都还没加载),只能放到底部或者doc的ready事件里了。最佳实践是有一个全屏的loading画面,当fontSize计算好了之后再把真正的页面展示出来。
网上找的 不喜勿喷!

使用javascript对url编码和使用python进行解码

wangtong阅读(118)

本文讨论javascript和python的 url编码解码函数
如果一个url带有动态参数,那么这个url就很难被当做其他url的参数进行传递了,因为浏览器无法正确识别,这个时候就需要对url进行编码,把不是字母数字的字符转换成%的形式
javascript对url进行编码的函数有3个,escape,encodeURI,encodeURIComponent 推荐使用最后一个,因为encodeURIComponent()不编码的字符最少,只有5个
~!*()’
encodeURIComponent和decodeURIComponent 可以成对使用
对应python的函数是urllib.quote 和 urllib.unquote,也可以成对使用
在client端用encodeURIComponent编码,在服务器端可以用urllib.unquote解码

web app iphone4 iphone5 iphone6 响应式布局 适配代码

wangtong阅读(133)

现在满大街的APP,除了游戏,软件图形类的需要用原生开发好点。现在大多还是基于WEBAPP或者混合的hybrid app,大家都知道资讯类的小应用其实网页就可以胜任,当然如果你要调用一些应设备,原生的APP外hybrid app也是一个不错的选择。不过我们今天的主角是WEB APP,WEB APP好处就是,随时随地有网就能看,简单实用。对于开发来说,更是高效率低成本,当然对于追求细致的来说。。。就有点。。。。。好了,下面我们来看看WEB APP怎么区分iphone 4 5 6吧
那么-webkit-min-device-pixel-ratio:2可以用来区分iphone(4/4s/5)和其它的手机
iPhone4/4s的分辨率为640960 pixels,DPI为是320480,设备高度为480px
iPhone5的分辨率为6401136 pixels,DPI依然是320568,设备高度为568px
iPhone6的分辨率为7501334 pixels,DPI依然是375667,设备高度为667px
iPhone6 Plus的分辨率为1242×2208 pixels,DPI依然是414*736,设备高度为736px
那么我们只需要判断iphone手机的device-height(设备高)值即可区别iPhone4和iPhone5、iPhone6、iPhone6 Plus使用css
通过 CSS3 的 Media Queries 特性,可以写出兼容iPhone4和iPhone5、iPhone6、iPhone6 Plus的代码~~
方式一,直接写到样式里面

<font face="宋体"><font style="font-size:12px"><font color="Black"><strong><font face="宋体"><font style="font-size:12px"><font color="Black"><strong>@media (device-height:480px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone4/4s */
  
    .class{}
  
}
  
@media (device-height:568px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone5 */
  
    .class{}
  
}
  
  
@media (device-height:667px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone6 */
  
    .class{}
  
}
  
@media (device-height:736px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone6 Plus */
  
    .class{}
  
}</strong></font></font></font></strong></font></font></font>

方式二,链接到一个单独的样式表,把下面的代码放在标签里

<font face="宋体"><font style="font-size:12px"><font color="Black"><strong><font face="宋体"><font style="font-size:12px"><font color="Black"><strong><link rel="stylesheet" media="(device-height: 480px) and (-webkit-min-device-pixel-ratio:2)" href="iphone4.css">
  
<link rel="stylesheet" media="(device-height: 568px)and (-webkit-min-device-pixel-ratio:2)" href="iphone5.css">
  
<link rel="stylesheet" media="(device-height: 667px)and (-webkit-min-device-pixel-ratio:2)" href="iphone6.css">
  
<link rel="stylesheet" media="(device-height: 736px)and (-webkit-min-device-pixel-ratio:2)" href="iphone6p.css"></strong></font></font></font></strong></font></font></font>

使用Js

<font face="宋体"><font style="font-size:12px"><font color="Black"><strong><font face="宋体"><font style="font-size:12px"><font color="Black"><strong>//通过高度来判断是否是iPhone 4还是iPhone 5或iPhone 6、iPhone6 Plus
  
isPhone4inches = (window.screen.height==480);
  
isPhone5inches = (window.screen.height==568);
  
isPhone6inches = (window.screen.height==667);
  
isPhone6pinches = (window.screen.height==736);</strong></font></font></font></strong></font></font></font>

jq 日期插件

wangtong阅读(132)

在工作中遇到的,需要做一个日期插件,所以整合了一下,分享一下,供大家使用。 效果如下: 做的并不完善,希望大神们可以帮着完善并分享至本模块。一起进步。
如果有啥意见建议可以留言!
代码地址如下:

代码地址

javascript(window.onresize)事件获取窗口大小

wangtong阅读(83)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>请调整浏览器窗口</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
    <h2 align="center">请调整浏览器窗口大小</h2><hr>
    <form action="#" method="get" name="form1" id="form1">
        <!--显示浏览器窗口的实际尺寸-->
        浏览器窗口 的实际高度: <input type="text" name="availHeight" size="4"><br>
        浏览器窗口 的实际宽度: <input type="text" name="availWidth" size="4"><br>
    </form>
    <script type="text/javascript">
    <!--
        var winWidth = 0;
        var winHeight = 0;
        function findDimensions() //函数:获取尺寸
        {
             //获取窗口宽度
             if (window.innerWidth)
                   winWidth = window.innerWidth;
             else if ((document.body) && (document.body.clientWidth))
                   winWidth = document.body.clientWidth;
             //获取窗口高度
             if (window.innerHeight)
                   winHeight = window.innerHeight;
             else if ((document.body) && (document.body.clientHeight))
                   winHeight = document.body.clientHeight;
            
             //通过深入Document内部对body进行检测,获取窗口大小
             if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth)
             {
                 winHeight = document.documentElement.clientHeight;
                 winWidth = document.documentElement.clientWidth;
             }
              
             //结果输出至两个文本框
             document.form1.availHeight.value= winHeight;
             document.form1.availWidth.value= winWidth;
        }
        findDimensions();                  //调用函数,获取数值
        window.onresize=findDimensions;
    //-->
    </script>
</body>
</html>

SlipJs API详细

wangtong阅读(120)

refresh(core_width, width):

当在滚动对象的大小发生改变时(如:当用户滚动到底部时你通过ajax加载新内容,这时元素的尺寸会改变)必须调用refresh方法。主要用处是让SlipJs及时感知尺寸的变化以便正常工作。

// 参数说明:这两个参数都是可选的。
// core_width: 滑动元素的宽度或者高度,单位为px(横向滑动时传递宽度,纵向滑动时传递高度)
// width:      滑动元素的父级元素的宽度或者高度,单位为px(横向滑动时传递宽度,纵向滑动时传递高度)
// 例如:
var slipjs = slipjs('px',core);
 
// 当元素的尺寸改变时,我们需要将新的尺寸给refresh:
slipjs.refresh(1000,400);
// 也可以不传递参数,这时SlipJs将自己通过获取元素的实际尺寸。

toPage(num, time):

page换屏状态下是用toPage可以滑动到指定屏数。

// 参数说明:
// num  : 要到达的屏数
// time : 设定过程花费的时间,单位ms
// 例如 :
var slipjs = slip('page', core, {
    num: 3
});
 
// 这时我们想要用3秒的时间缓慢地轮换到第3屏就可以这样:
slipjs.toPage(3, 3000);

that.page

可在page换屏状态的startFun moveFun touchEndFun endFun 这4个函数中获取当前所在的页(屏)数。

function end() {
    alert(this.page);// 在滑动结束后输出当前的页(屏)数
}
slip('page', bar_list_div, {
    num: 3,
    endFun: end
});

提示:该api在图片轮换中导航小点的变化需要用到该api。
注释:关于startFun moveFun touchEndFun endFun 这4个函数的用法详见:SlipJs快速使用教程
this.xy
当你想在 startFun moveFun touchEndFun endFun 这4个函数中获取滑动元素的坐标时可直接使用 this.xy 来获取,当状态为横向滑动时该值是滑动元素的x坐标,当状态为纵向滑动时该值是滑动元素的y坐标。
例如:

function move() {
    console.log(this.xy);// 在滑动过程中输出滑动元素当前的y坐标
}
slip('px', bar_list_div, {
    moveFun: move
});

提示:该api在“下拉刷新”的例子中使用到,在加载数据时非常有用。
this.wide_high(3.0.1开始支持)
当你想在 startFun moveFun touchEndFun endFun 这4个函数中获取滑动元素的宽或者高(横向滑动的时候是宽,上下滑动时是高)可直接使用 this.wide_high来获取,当状态为横向滑动时该值是滑动元素的width,当状态为纵向滑动时该值是滑动元素的height。例如:

function move() {
    console.log(this.wide_high);// 在滑动过程中输出滑动元素当前的y坐标
}
slip('px', bar_list_div, {
    moveFun: move
});

this.parent_wide_high(3.0.1开始支持)

当你想在 startFun moveFun touchEndFun endFun 这4个函数中获取滑动元素父级元素的宽或者高(横向滑动的时候是宽,上下滑动时是高)可直接使用 this.parent_wide_high来获取,当状态为横向滑动时该值是父级元素的width,当状态为纵向滑动时该值是父级元素的height。例如:

function move() {
    console.log(this.parent_wide_high);// 在滑动过程中输出滑动元素当前的y坐标
}
slip('px', bar_list_div, {
    moveFun: move
});

backward和forward(3.5.0开始支持)
以图片轮换为例,现在你想要添加两个按钮,一个按钮为“上一张”,另一个为“下一张”,那么这是你就可以是使用backward和forward了,具体使用方法如下:

var slip_img = slip('page',change_screen_ul,{
        change_time: 5000,
                      num: 3
});
document.getElementById("backward").onclick = function(){
    slip_img.backward(); // 上一张
}
document.getElementById("forward").onclick = function(){
    slip_img.forward(); // 下一张
}

SlipJs快速使用教程

wangtong阅读(154)

之前有一个拖动排序的jq插件,但是有弊端不能在移动端生效,今天找到一个slipjs,在移动端可以执行。
进入正题 如何使用SlipJs,很简单只要两步:
提示:具体如何使用SlipJs实现演示例子slipjs.com/demo中的效果,请下载并参考演示例子的源码,下载地址:www.slipjs.com
第一步: 载入slip.js,你只需下载并在你的页面中载入slip.js;
第二步: 使用函数slip(mode, core, para);
该函数有三个参数:  
第一个参数:mode ,可以为”px”或者”page”。
为”px” 时将实现惯性滚动的效果(类似slipjs.com/dome中的第一个例子),
为”page” 时将实现类似图片轮换的换屏效果(类似slipjs.com/dome中的第一个例子)。
例如:

slip("px",  core,  para); 
// 或者
slip("page",  core,  para);

第二个参数:core ,这个参数传递的是运动的对象,例如:

var core = document.getElementById("core");
slip('px', core, para);

第三个参数:para ,这个参数是一个对象子面量,该参数在px惯性滚动状态下和在page换屏状态下可传递的内容存在差别。
首先是这两种状态下均可传递的参数有:

slip('px', core, {
        startFun: function,   // 触摸开始时执行的函数
         moveFun: function,   // 触摸过程中执行的函数
     touchEndFun: function,   // 触摸结束时执行的函数
          endFun: function    // 滑动结束时执行的函数
});

page换屏状态下独有的参数:

slip('page', core, {
              num: 3,        // 屏数,例如轮换图片是图片的张数(必需)
      change_time: 3000,     // 图片自动轮换的时间
      lastPageFun: function, // 用户在滑动到最后一屏后仍然滑动下一屏时执行的函数(可用于图片浏览时使用),更详细请看下文
     firstPageFun: function, // 用户在滑动到第一屏后仍然滑动上一屏时执行的函数(可用于图片浏览时使用),更详细请看下文
        no_follow: true      // 是否跟随手指移动来完成换屏,ture为不跟随,默认跟随,更详细请看下文
             loop: true      // slipjs3.5.0开始支持,可以实现无限滑动下一张(最后一张时继续滑可以回到第一张)
});

px惯性滚动状态下独有的参数:

slip('px', core, {
       direction: 'x',   // 设置滑动方向,"x"为横向滑动,默认为纵向滑动。
         perfect: true,  // 是否启用完美模式,true为启用,何为完美模式,更详细请看下文
          no_bar: true,  // 是否启用模仿滚动条,true为不启用,默认启用
     bar_no_hide: true,  // 是否在滚动停止时隐藏滚动条,true为不隐藏,默认隐藏
      core_width: 1000,  // 滑动元素的宽度或者高度,单位为px(横向滑动时传递宽度,纵向滑动时传递高度),更详细请看下文
           width: 400,   // 滑动元素的父级元素的宽度或者高度,单位为px(横向滑动时传递宽度,纵向滑动时传递高度),更详细请看下文
         bar_css: "background-color: rgba(8, 97, 149, 0.5);"  // 自定义滚动条的样式
});

现在你就可以预览效果了。
更详细的参数说明
看到这里如果你存在疑问那我猜可能会集中在几个参数的用法上,这些参数包括
page换屏状态下的

lastPageFun: function, // 用户在滑动到最后一屏后仍然滑动下一屏时执行的函数(可用于图片浏览时使用),更详细请看下文
firstPageFun: function, // 用户在滑动到第一屏后仍然滑动上一屏时执行的函数(可用于图片浏览时使用),更详细请看下文
   no_follow: true      // 是否跟随手指移动来完成换屏,ture为不跟随,默认跟随,更详细请看下文

px惯性滚动状态下的

perfect: true,  // 是否启用完美模式,true为启用,何为完美模式,更详细请看下文

接下来我将一个个详细讲解

lastPageFun: function // 用户在滑动到最后一屏后仍然滑动下一屏时执行的函数。
//你想象一下:你现在是用px换屏模式来做一个图片放大并可滑动查看下一张的功能,这时你的用户滑动到了
最后一张还想继续查看下一张,你想提醒用户已经到最后一张了,那么这些提示的代码就可以放在这个函数中。
no_follow: true      // 是否跟随手指移动来完成换屏,ture为不跟随,默认跟随。以图片轮换为例,
你可能发现现在互联网上的手机网站图片轮换功能基本可以分为两类,一类是你手指在上面滑动的时候图片会跟随你手指移动,
另一类是你手指在上面滑动时没有反应,只有你手指离开的时候吹滑动到下一张。通过这个参数你可以任选一种适合你的方式。
perfect: true  // 是否启用完美模式,true为启用,何为完美模式。所谓完美模式是对应流畅模式而言的,
完美模式和流畅模式的区别表现在于滚动条的变化,完美模式下滚动条在滚动到底部或者顶部的时候大小会变化,
而流畅模式则没这个视觉效果。理论上流畅模式要比完美模式反应效率等各方面更快,其中的区别在表现上是滚
动条的区别,其实是实现形式的不同使得流畅模式的无法实现这一视觉效果。