不二 发布的文章

HTML5学习之Communication API

Communication,顾名思义,是交流,是通信。在HTML5的页面,框架之间通信中,Communication API扮演着非常重要的作用。

既然是交流,必然有发送和接收2个过程。

在Communication API中,可以使用postMessage方法进行发送数据。接收的话只要监听一个名为messge的事件就可以了。

下面是一个例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>html5 页面发送信息测试</title>
    <script>
        function load(){
            var postBtn=document.getElementById("postMessage");
            postBtn.addEventListener("click",post,true);
        }
        function post(){
            var frame=document.getElementById("frame").contentWindow;
            frame.postMessage("hello world","*");
        }
    </script>
</head>
<body onload="load()">
<h2>html5 页面发送信息测试</h2>
<iframe id="frame" src="communication2.html"></iframe>

<button id="postMessage">发送信息</button>
</body>
</html>

在上述代码中,我们向id为frame的iframe发送了消息,那么communication2.html是如何接收呢?
上代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>html5 页面发送信息测试-接收端</title>
    <script>
        function load(){
            window.addEventListener("message",getMessage,true);
        }
        function getMessage(e){
            var message=document.getElementById("messageText");
            message.innerHTML="源:"+e.origin+"<br/>内容:"+e.data;
        }
    </script>
</head>
<body onload="load()">
<h2>html5 页面发送信息测试-接收端</h2>
<p id="messageText"></p>
</body>
</html>

使用addEventListener监听message事件,当有消息,就会执行getMessage回调。

就是这么简单!

HTML5学习之Geolocation

以前如果要获取互联网用户所在地都是根据用户的IP地址来获取地理位置,这样获取到的数据和真实数据有很大的偏差。为了获取更加精确的位置,可以使用了html5的geolocation来获取经纬度,然后再获取所在地理位置,如何获取,我在下面会说到。先说下基本概念。

  Geolocation在的navigator 对象中,我们可以通过 navigator.geolocation 来使用它。不支持 geolocation 的浏览器并不包含这一对象,那么可以通过下面的代码来做能力检测,对不同的浏览器做不同的处理。在访问 geolocation 对象时,即调用 geolocation 下面的方法时,浏览器会弹出提示,询问用户是否许可网站提供的位置服务,只有在得到用户许可过后,服务才会继续,否则将被停止。 

  常用的navigator.geolocation对象有以下三种方法:

  1. 获取当前地理位置:navigator.geolocation.getCurrentPosition(success_callback_function,
    error_callback_function, position_options)
  2. 持续获取地理位置:navigator.geolocation.watchPosition(success_callback_function,
    error_callback_function, position_options)
  3. 清除持续获取地理位置事件:navigator.geolocation.clearWatch(watch_position_id)

  参数position_options是配置项,由JSON格式传入:

  enableHighAccuracy:true/false,它将告诉浏览器是否启用高精度设备,所谓的高精度设备包含但不局限于前面所提到的 GPS 和 WIFI,值为 true 的时候,浏览器会尝试启用这些设备,默认指为 true,在这种情况下,浏览器会尽可能地进行更为精确的查询,简单地说,如果用户有可用的 GPS 设备,会返回 GPS 设备的查询结果,IP 是最后的选择,对于移动设备来说,网络接入点(基站)或许成为另一个选择,对此我还没有完全了解,但根据测试,即时没有任何额外功能的手机,也能够得到更为精确的查询结果。

  maximumAge:单位毫秒,告诉设备缓存时间,主要用于设备的省电或者节省带宽方面。

  timeout:单位毫秒,超时事件,获取位置信息时超出设定的这个时长,将会触发错误,捕获错误的函数将被调用,并且错误码指向TIMEOUT。

var position_option = {
                enableHighAccuracy: true,
                maximumAge: 30000,
                timeout: 20000
            };
navigator.geolocation.getCurrentPosition(getPositionSuccess, getPositionError, position_option);
function getPositionSuccess( position ){
        var lat = position.coords.latitude;
        var lng = position.coords.longitude;
        alert( "您所在的位置: 纬度" + lat + ",经度" + lng );
        if(typeof position.address !== "undefined"){
                var country = position.address.country;
                var province = position.address.region;
                var city = position.address.city;
                alert(' 您位于 ' + country + province + '省' + city +'市');
        }
}
coords其他返回信息:

coords.accuracy:返回经纬度的精度(米)

coords.speed :速度

coords.altitude :当前的高度,海拔(米)

coords.altitudeAccuracy:高度的精度(米)

coords.heading:朝向

function getPositionError(error) {
    switch (error.code) {
        case error.TIMEOUT:
            alert("连接超时,请重试");
            break;
        case error.PERMISSION_DENIED:
            alert("您拒绝了使用位置共享服务,查询已取消");
            break;
        case error.POSITION_UNAVAILABLE:
            alert("获取位置信息失败");
            break;
    }
}

在获取城市信息的时候只有firefox会提供城市地址。chrom不会给出。当然sogou地图提供了获取位置信息的api。

链接地址:http://map.sogou.com/api/documentation/javascript/api2.5/interface_regeocode.html  

移动设备持续获取地理位置方法:navigator.geolocation.watchPosition

对于使用移动设备的用户来说,位置并不是固定的,W3C 当然也考虑到了这一点,watchPosition 是一个专门用来处理这一情况的方法,watchPosition 被调用后,浏览器会跟踪设备的位置,每一次位置的变化,watchPosition 中的代码都将会被执行。对于致力于移动设备 web 开发的同学来说,这个方法是及其重要的,它也许将会改变 web 移动客户端的格局。
使用navigator.geolocation.clearWatch既可以清除navigator.geolocation.watchPosition的监控事件。

CORS-AJAX POST跨域解决方案

前言

跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略,即JavaScript或Cookie只能访问同域下的内容),因为我们在日常的项目开发时会不可避免的需要进行跨域操作,所以跨域能力也算是前端工程师的基本功之一。

和大多数跨域的解决方案一样,JSONP也是我的选择,可是某天PM的需求变了,某功能需要改成支持POST,因为传输的数据量比较大,GET形式搞不定。所以折腾了下闻名已久的CORS(跨域资源共享,Cross-Origin
Resource Sharing),这边文章也就是折腾期间的小记与总结。

概述

  1. CORS能做什么:

    正常使用AJAX会需要正常考虑跨域问题,所以伟大的程序员们又折腾出了一系列跨域问题的解决方案,如JSONP、flash、ifame、xhr2等等。
    本文介绍的CORS就是一套AJAX跨域问题的解决方案。

  2. CORS的原理:

    CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX
    请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。

    CORS浏览器支持情况如下图:

06082033-9233ced009a644f9af909baca57a72b7.png

06082103-f46e697e71884240b7200f8713697fbc.png

喜闻乐见、普大喜奔的支持情况,尤其是在移动终端上,除了opera Mini;PC上的现代浏览器都能友好的支持,除了IE9-,不过前端工程师对这种情况早应该习惯了...

CORS启航

  假设我们页面或者应用已在 http://www.test1.com 上了,而我们打算从 http://www.test2.com 请求提取数据。一般情况下,如果我们直接使用 AJAX 来请求将会失败,浏览器也会返回“源不匹配”的错误,"跨域"也就以此由来。
  利用 CORS,http://www.test2.com 只需添加一个标头,就可以允许来自 http://www.test1.com 的请求,下图是我在PHP中的 hander() 设置,“*”号表示允许任何域向我们的服务端提交请求:

06082159-b1a102a3ce0e49e7841f76675236e408.png

  也可以设置指定的域名,如域名 http://www.test2.com ,那么就允许来自这个域名的请求:

06082227-10f22c4f75094faeb78255509199aed5.png

  当前我设置的header为“”,任意一个请求过来之后服务端我们都可以进行处理&响应,那么在调试工具中可以看到其头信息设置,其中见红框中有一项信息是“Access-Control-Allow-Origin: ”,表示我们已经启用CORS,如下图。
  

PS:由于demo都在我厂的两台测试机间完成,外网也不能访问,所以在这就不提供demo了,见谅

06082242-c70a37f237ed48c4a60d33fccfd467fb.png

  简单的一个header设置,一个支持跨域&POST请求的server就完成了:)

  当然,如果没有开启CORS必定失败的啦,如下图:

06110611-953e174761e545ad94da63fceee9afb4.jpg

问题&小结

  • 刚刚说到的兼容性。CORS是W3C中一项较新的方案,所以部分浏览器还没有对其进行支持或者完美支持,详情可移http://www.w3.org/TR/cors/
  • 安全问题。CORS提供了一种跨域请求方案,但没有为安全访问提供足够的保障机制,如果你需要信息的绝对安全,不要依赖CORS当中的权限制度,应当使用更多其它的措施来保障,比如OAuth2。

自认为的cors使用场景:

  1. cors在移动终端支持的不错,可以考虑在移动端全面尝试;
  2. PC上有不兼容和没有完美支持,所以小心踩坑。当然浏览器兼容就是个伪命题,说不准某个浏览器的某个版本就完美兼容了,说不准就有点小坑,尼玛伤不起!~
  3. jsonp是get形式,承载的信息量有限,所以信息量较大时CORS是不二选择;
  4. 配合新的JSAPI(fileapi、xhr2等)一起使用,实现强大的新体验功能。

第六章:依赖管理《Developing JavaScript Web Applications》学习笔记

为什么要依赖管理

没有依赖管理,代码会变得混乱不堪,如下:

<script src="jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="jquery.ui.js" type="text/javascript" charset="utf-8"></script>
<script src="application.utils.js" type="text/javascript" charset="utf-8"></script>
<script src="application.js" type="text/javascript" charset="utf-8"></script>
<script src="models/asset.js" type="text/javascript" charset="utf-8"></script>
<script src="models/activity.js" type="text/javascript" charset="utf-8"></script>
......

依赖管理系统除了能解决实际的编程复杂度和可维护性的问题,还能解决性能方面的问题。浏览器需要针对每个JavaScript 文件都发起一个HTTP 请求,尽管可以将这些请求放入异步队列,但大量的HTTP 连接总会造成性能的下降,每个连接都包含额外的HTTP 头信息、Cookie,并都要做TCP 的三次握手。

- 阅读剩余部分 -

第五章:视图和模板《Developing JavaScript Web Applications》学习笔记

模板

jQuery.tmpl是jQuery官方指定的jQuery模板库,这个库有一个主要的函数jQuery.tmpl(),可以给它传入一个模板和一些数据,函数会返回渲染好的元素节点,可以将渲染的结果追加至页面里。如果数据是数组的话,对于数组中的每个数据项都会生成渲染好的模板,否则,将只会渲染一个模板:

var object = {
url: "http://example.com",
getName: function(){ return "Trevor"; }
};
var template = '<li><a href="${url}">${getName()}</a></li>';
var element = jQuery.tmpl(template, object);
// 得到的结果: <li><a href="http://example.com">Trevor</a></li>
$("body").append(element);

这里你可以看到我们使用${} 语法来书写插见的变量。不管括号中的变量名是什么,都会根据传入jQuery.tmpl() 的对象来计算得出要填充的文本,不考虑它是一种属性还是一个函数。
然而模板的功能要比这种纯粹的插值替换强大很多。很多模板库都具有一些高级功能,诸如条件流(conditional flow)和迭代。你可以通过使用if 和else 语句来实现条件流,就像用纯粹的JavaScript 写出的代码一样。惟一和JavaScript 语法不同的地方是,这里需将关键字用双括号括起来,以便模板引擎能正确识别它们:

{{if url}}
${url}
{{/if}}

遍历是所有模板类库都提供的基础功能。使用模板类库的{{each}} 关键字可以遍历任何JavaScript 类型,包括Object 和Array。你可以使用$value 变量来访问当前正被遍历的值

var object = {
foo: "bar",
messages: ["Hi there", "Foo bar"]
};

然后使用接下来的模板来遍历这个message 数组,显示每条消息。此外,数组元素的索引也可以使用$index 变量来输出:

<ul>
{{each messages}}
<li>${$index + 1}: <em>${$value}</em></li>
{{/each}}
</ul>

模板Helpers

有时在视图内部使用“通用helper 函数”(generic helper function)是非常好用的,比如格式化一个日期或数字。将它抽象出来,并用命名空间进行管理,而不是直接将函数掺杂进视图中,这样才能保持逻辑和视图之间的解耦。例如:

// helper.js
var helper = {};
helper.autoLink = function(data){
var re = /((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g;
return(data.replace(re, '<a target="_blank" href="$1">$1</a> ') );
};
// template.html
<div>
${ helper.autoLink(this.data) }
</div>

这里还有一个额外的好处,autoLink() 函数是通用的,在应用的任何地方都可以重用它。

模板存储

说到模板存储,有这样一些内容需要考虑:

  1. 在JavaScript 中以行内形式存储。---违背了MVC 架构的原则
  2. 在自定义 script 标签里以行内形式存储。--推荐这种方式,浏览器不必对它们进行渲染,而仅将它们解析为内容文本
  3. 远程加载。--影像UI 的渲染
  4. 在 HTML 中以行内形式存储。--增加了初始页面的体积

其中一些非常适合在MVC 架构中使用。作者推荐使用第2种方式,即在自定义script标签里以行内形式存储模板。例如:

<script type="text/x-jquery-tmpl" id="someTemplate">
<span>${getName()}</span>
</script>
<script>
var data = {
getName: function(){ return "Bob" }
};
var element = $("#someTemplate").tmpl(data);
element.appendTo($("body"));
</script>

绑定

我们希望,当模型记录创建、更新或销毁时,都会触发change 事件,并重新渲染视图。在下面的例子中,我们创建了一个基础的
User 类,新建了事件绑定和触发,最后监听了change 事件,当触发change 事件时重新渲染视图:

<script>
var User = function(name){
this.name = name;
};
User.records = []
User.bind = function(ev, callback) {
var calls = this._callbacks || (this._callbacks = {});
(this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);
};
User.trigger = function(ev) {
var list, calls, i, l;
if (!(calls = this._callbacks)) return this;
if (!(list = this._callbacks[ev])) return this;
jQuery.each(list, function(){ this() })
};
User.create = function(name){
this.records.push(new this(name));
this.trigger("change")
};
jQuery(function($){
User.bind("change", function(){
var template = $("#userTmpl").tmpl(User.records);
$("#users").empty();
$("#users").append(template);
});
}):
</script>
<script id="userTmpl" type="text/x-jquery-tmpl">
<li>${name}</li>
</script>
<ul id="users">
</ul>

现在无论何时修改User 的记录,User 的模型的change 事件都会被触发,调用我们模板的回调函数并重绘用户列表。这很有帮助,因为我们只需关注创建和更新用户的记录,而不必担心视图的更新,视图的更新是自动的。比如,我们创建一个新的User :

User.create("Sam Seaborn");