本指南提供了开发JavaScriptSDK的简介。javascript
描述SDK的最好的一句话是:“ SDK是弥合用户和(浏览器)计算机之间差距的链接。”
经过使用本指南,SDK将可以在浏览器,台式机,移动网络和各类其余可以运行JavaScript的平台上运行。html
本文的目标受众暂时不包括非浏览器环境,例如硬件,嵌入式和Node.js。可是,未来会添加一些材料来覆盖这些区域。html5
答案显而易见,可是这里仍是要重申一下。java
“ 软件开发工具包的简称,一种代码包,使开发人员可以为特定平台开发应用程序。SDK一般包括一个或多个API,编程工具和文档。”node
根据SDK服务的目的和用途,常见的共享特征包括但不限于本地的,简短的,快速的,简洁的,可读的和可测试的。jquery
普遍采用的良好作法是使用原生JavaScript编写SDK。不建议使用编译为JavaScript的语言,例如LiveScript,CoffeeScript,TypeScript等。git
还建议不要在SDK开发中使用诸如jQuery之类的库。若是非要用于DOM操做,还有其余相似jQuery的库如zepto.js 等可供选择。github
若是有HTTP ajax请求要求,则用原生方法如window.fetch。它更轻巧,并在不断增加的平台中获得支持。ajax
向后兼容性相当重要。每一个新的SDK版本都应向后兼容。一样,当前版本应设计为支持未来的SDK版本。这称为渐进加强。算法
此外,良好的文档,良好的注释代码,良好的单元测试覆盖范围以及端到端(用户)方案是SDK成功的关键。
设计JavaScript SDK时须要考虑如下三个用例:
编写一个JavaScript环境中使用SDK的示例是有必要的。
为了将SDK包含在面向用户的环境中,使用异步语法加载脚本是一个好习惯。
这有助于优化使用SDK的网站上的用户体验。这种方法减小了SDK库干扰主要内容加载的机会。
异步语法
<script> (function () { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'http://<DOMAIN>.com/sdk.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); </script>
针对现代浏览器时使用async
语法。
<script async src="http://<DOMAIN>.com/sdk.js"></script>
传统语法
<script type="text/javascript" src="http://<DOMAIN>.com/sdk.js"></script>
比较
这是显示异步和传统语法之间区别的简单图形。
异步:
|----A-----| |-----B-----------| |-------C------|
同步:
|----A-----||-----B-----------||-------C------|
异步和延迟的JavaScript执行说明
https://developers.google.com...
避免或使用压缩过的阻塞JavaScript(尤为是在执行前必须先获取的外部脚本)是一种很好的作法。能够内联呈现页面内容所需的脚本,以免额外的网络请求,可是内联的内容必须很小,而且必须快速执行(非阻塞方式)以提供良好的性能。对于初始渲染不重要的脚本,应使其异步或推迟到第一次渲染以后进行。
异步的问题
使用异步方法时,建议在首屏中加载,解析和执行全部库以前执行SDK初始化功能。
考虑如下代码段做为上一个语句的直观示例:
<script> (function () { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'http://<DOMAIN>.com/sdk.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); // execute your script immediately here SDKName('some arguments'); </script>
这种初始化终将致使错误。此时SDKName()
未定义的函数在环境的全局变量中可用以前执行。该脚本还没有加载。
为了定期运行,须要一些技巧来确保脚本成功执行。该事件将(须要)存储在SDKName.q
队列数组中。SDK应该可以处理和执行SDKName.q
事件并初始化SDKName
命名空间。
如下代码段描述了上一段中的声明。
<script> (function () { // add a queue event here SDKName = SDKName || function () { (SDKName.q = SDKName.q || []).push(arguments); }; var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'http://<DOMAIN>.com/sdk.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); // execute your script immediately here SDKName('some arguments'); </script>
或使用[].push
<script> (function () { // add a queue event here SDKName = window.SDKName || (window.SDKName = []); var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'http://<DOMAIN>.com/sdk.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); // execute your script immediately here SDKName.push(['some arguments']); </script>
其余
还有其余导入脚本的方法
在ES2015中导入
import "your-sdk";
模块化导入脚本
这里有完整的源代码,而这本很棒的教程 "Loading JavaScript Modules" 可能有助于深刻理解上面讨论的概念。
module('sdk.js',['sdk-track.js', 'sdk-beacon.js'],function(track, beacon) { // sdk definitions, split into local and global/exported definitions // local definitions // exports }); // you should contain this "module" method (function () { var modules = {}; // private record of module data // modules are functions with additional information function module(name,imports,mod) { // record module information window.console.log('found module '+name); modules[name] = {name:name, imports: imports, mod: mod}; // trigger loading of import dependencies for (var imp in imports) loadModule(imports[imp]); // check whether this was the last module to be loaded // in a given dependency group loadedModule(name); } // function loadModule // function loadedModule window.module = module; })();
使用如下版本控制样式之一不是一个好习惯:
brand-v<timestamp>.js
brand-v<datetime>.js
brand-v1-v2.js
,缘由是跟踪最新版本变得混乱。所以,之前的样式不能帮助使用SDK的开发人员。
可是,在对SDK进行版本控制时,最好使用Semantic Versioning(也称为SemVer)。它具备三个主要部分,每一个部分与发行版的重要性相对应:“ MAJOR.MINOR.PATCH”。例如,版本v1.0.0
v1.5.0
v2.0.0
易于在changelog文档中进行跟踪。
根据服务设计,能够按版本发布(或跟踪)SDK的一些方法以下:
http://<DOMAIN>.com/sdk.js?v=1.0.0
http://<DOMAIN>.com/v1.0.0/sdk.js
http://v1.<DOMAIN>.com/sdk.js
根据用例,一般建议使用其余依赖于环境的表单:
在stable
版本中http://<DOMAIN>.com/sdk-stable.js
在unstable
版本中http://<DOMAIN>.com/sdk-unstable.js
在alpha
版本中http://<DOMAIN>.com/sdk-alpha.js
在latest
版本中http://<DOMAIN>.com/sdk-latest.js
在experimental
版本中http://<DOMAIN>.com/sdk-experimental.js
建议阅读:Why use SemVer? 在npm
博客上。
当没有发布公告时,很难注意到SDK是否已更新(或升级)。编写变动日志以记录主要,次要甚至错误修复的更改是一个好习惯。跟踪SDK API中的更改可提供良好的开发人员体验。- Keep a Changelog(Github Repo)
每一个版本应具备:
[Added] for new features. [Changed] for changes in existing functionality. [Deprecated] for once-stable features removed in upcoming releases. [Removed] for deprecated features removed in this release. [Fixed] for any bug fixes. [Security] to invite users to upgrade in case of vulnerabilities.
另外,commit-message-emoji使用表情符号来解释提交自己的更改。
为避免与其余库冲突,最好定义一个以上的全局SDK命名空间。命名也应避免将经常使用的单词和流行语用做命名空间。
举个简单的例子,SDK Playground能够很好地使用(function () { ... })()
或ES6块{ ... }
包装全部源。
这是许多流行的JavaScript库(如jQuery,Node.js等)中愈来愈常见的一种作法。此方法会在文件的整个内容周围建立一个闭包,这多是最重要的是,建立一个私有命名空间,从而有助于避免不一样JavaScript模块和库之间可能发生的名称冲突。#
为了不命名冲突
在Google Analytics中,经过更改值来定义名称空间ga
(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','//www.google-analytics.com/analytics.js','ga');
根据OpenX experience的经验,支持一个参数来请求名称空间。
<script src="http://your_domain/sdk?namespace=yourcompany"></script>
Cookie
当使用subdomain
和path
时,使用cookie的域范围很是复杂。
对于path=/
,在http://github.com
中有一个cookiefirst=value1
,而在http://sub.github.com
中有另外一个cookiesecond=value2
http://github.com | http://sub.github.com | |
---|---|---|
first=value1 | ✓ | ✓ |
second=value2 | ✘ | ✓ |
There is a cookiefirst=value1
in domainhttp://github.com
, cookiesecond=value2
in domain pathhttp://github.com/path1
and cookiethird=value3
in domainhttp://sub.github.com
,
http://github.com | http://github.com/path1 | http://sub.github.com | |
---|---|---|---|
first=value1 | ✓ | ✓ | ✓ |
second=value2 | ✘ | ✓ | ✘ |
third=value3 | ✘ | ✘ | ✓ |
检查Cookie是否可写
给定一个域(默认为当前主机名),检查cookie是否可写。
var checkCookieWritable = function(domain) { try { // Create cookie document.cookie = 'cookietest=1' + (domain ? '; domain=' + domain : ''); var ret = document.cookie.indexOf('cookietest=') != -1; // Delete cookie document.cookie = 'cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT' + (domain ? '; domain=' + domain : ''); return ret; } catch (e) { return false; } };
检查第三方Cookie是否可写
仅使用客户端JavaScript进行检查是不可能的,可是服务器能够帮助实现这一点。
写入/读取/删除Cookie代码
写入/读取/删除Cookie脚本的代码段。
var cookie = { write: function(name, value, days, domain, path) { var date = new Date(); days = days || 730; // two years path = path || '/'; date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = '; expires=' + date.toGMTString(); var cookieValue = name + '=' + value + expires + '; path=' + path; if (domain) { cookieValue += '; domain=' + domain; } document.cookie = cookieValue; }, read: function(name) { var allCookie = '' + document.cookie; var index = allCookie.indexOf(name); if (name === undefined || name === '' || index === -1) return ''; var ind1 = allCookie.indexOf(';', index); if (ind1 == -1) ind1 = allCookie.length; return unescape(allCookie.substring(index + name.length + 1, ind1)); }, remove: function(name) { if (this.read(name)) { this.write(name, '', -1, '/'); } } };
Session
重要的是要知道在JavaScript中不可能读写Session。那是服务器的责任。服务器端团队应实施与session管理相关的用例。
页面session的持续时间只要浏览器处于打开状态,而且在页面从新加载和还原后仍然存在。在新标签或窗口中打开页面将致使启动新session。
LocalStorage
存储没有到期日期的数据,存储限制更大(至少5MB),而且信息永远不会传输到服务器。
从每一个localStorage的http和https在同一个域中不共享。在网站内部建立iframe并将postMessage其传递给他人。
HOW TO?
检查LocalStorage可写
并不是全部浏览器都支持window.localStorage,所以SDK在使用前应检查其是否可用。
var testCanLocalStorage = function() { var mod = 'modernizr'; try { localStorage.setItem(mod, mod); localStorage.removeItem(mod); return true; } catch (e) { return false; } };
Session Storage
存储一个会话的数据(关闭选项卡时数据丢失)。
检查SessionStorage可写
var checkCanSessionStorage = function() { var mod = 'modernizr'; try { sessionStorage.setItem(mod, mod); sessionStorage.removeItem(mod); return true; } catch (e) { return false; } }
在客户端浏览器中,有一些事件load unload on off bind....如下是一些polyfill供您处理全部不一样的平台。
Document Ready
在开始执行SDK函数以前,请确保整个页面已完成加载(就绪)。
// handle IE8+ function ready (fn) { if (document.readyState != 'loading') { fn(); } else if (window.addEventListener) { // window.addEventListener('load', fn); window.addEventListener('DOMContentLoaded', fn); } else { window.attachEvent('onreadystatechange', function() { if (document.readyState != 'loading') fn(); }); } }
DOMContentLoaded-在文档彻底加载和解析时触发,而无需等待样式表,图像和subframes完成加载load事件可用于检测页面已fully-loaded
Information from JS Tip -https://github.com/loverajoel/jstips/blob/master/_posts/en/javascript/2016-02-15-detect-document-ready-in-pure-js.md
Message事件
关于iframe和window之间的跨域通讯,请阅读API documentation。
// in the iframe parent.postMessage("Hello"); // string // ========================================== // in the iframe's parent // Create IE + others compatible event handler var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; // Listen to message from child window eventer(messageEvent,function(e) { // e.origin , check the message origin console.log('parent received message!: ',e.data); },false);
Post message数据应为String,要在JSON中进行更高级的使用,请使用JSON String。尽管现代的浏览器确实在参数上支持结构化克隆算法,但并不是全部浏览器都支持。
方向改变
检测设备方向变化
window.addEventListener('orientationchange', fn);
获取方向旋转度
window.orientation; // => 90, -90, 0
Screen portrait-primary, portrait-secondary, landscape-primary, landscape-secondary (Experimental)
// https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation var orientation = screen.orientation || screen.mozOrientation || screen.msOrientation;
禁止滚动
在网页中,使用CSS样式overflow: hidden,在某些移动网络中,此CSS无效,请使用JavaScript事件。
document.addEventListener('touchstart', function(e){ e.preventDefault(); }); // prevent scroll // or document.body.addEventListener('touchstart', function(e){ e.preventDefault(); }); // prevent scroll // use move if you need some touch event document.addEventListener('touchmove', function(e){ e.preventDefault(); }); // prevent scroll Request
咱们的SDK与服务器之间的通讯使用Ajax请求。最多见的用例是利用jQuery的ajax http请求与服务器进行通讯。好消息是,有一个更好的解决方案来实现这一目标。
使用Image Beacon要求浏览器执行GET方法request以获取图像。
你们应该永远记得添加时间戳(Cache Buster),以防止在浏览器中进行缓存。
(new Image()).src = 'http://<DOMAIN>.com/collect?id=1111';
关于GET Query String的一些注意事项,其长度限制为2048(基本上取决于不一样的浏览器和服务器)。如下技巧有助于处理超出长度限制的状况。
if (length > 2048) { // do Multiple Post (form) } else { // do Image Beacon }
使用encodeURI
或存在众所周知的问题encodeURIComponent
。可是,最好了解这两种方法如何工做。在下面阅读详细信息。
对于图像加载成功/错误回调
var img = new Image(); img.src = 'http://<DOMAIN>.com/collect?id=1111'; img.onload = successCallback; img.onerror = errorCallback;
可使用本机表单元素POST方法发送键值。
var form = document.createElement('form'); var input = document.createElement('input'); form.style.display = 'none'; form.setAttribute('method', 'POST'); form.setAttribute('action', 'http://<DOMAIN>.com/track'); input.name = 'username'; input.value = 'attacker'; form.appendChild(input); document.getElementsByTagName('body')[0].appendChild(form); form.submit();
该服务一般很复杂,尤为是在须要经过POST方法发送更多数据时。
function requestWithoutAjax( url, params, method ){ params = params || {}; method = method || "post"; // function to remove the iframe var removeIframe = function( iframe ){ iframe.parentElement.removeChild(iframe); }; // make a iframe... var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.onload = function(){ var iframeDoc = this.contentWindow.document; // Make a invisible form var form = iframeDoc.createElement('form'); form.method = method; form.action = url; iframeDoc.body.appendChild(form); // pass the parameters for( var name in params ){ var input = iframeDoc.createElement('input'); input.type = 'hidden'; input.name = name; input.value = params[name]; form.appendChild(input); } form.submit(); // remove the iframe setTimeout( function(){ removeIframe(iframe); }, 500); }; document.body.appendChild(iframe); }
requestWithoutAjax('url/to', { id: 2, price: 2.5, lastname: 'Gamez'});
嵌入在html中的iframe始终能够用于覆盖在页面内生成内容的用例。
var iframe = document.createElement('iframe'); var body = document.getElementsByTagName('body')[0]; iframe.style.display = 'none'; iframe.src = 'http://<DOMAIN>.com/page'; iframe.onreadystatechange = function () { if (iframe.readyState !== 'complete') { return; } }; iframe.onload = loadCallback; body.appendChild(iframe);
从iframe内移除多余的边距
<iframe src="..." marginwidth="0" marginheight="0" hspace="0" vspace="0" frameborder="0" scrolling="no"></iframe>
将HTML内容放入iframe
<iframe id="iframe"></iframe> <script> var html_string= "content <script>alert(location.href);</script>"; document.getElementById('iframe').src = "data:text/html;charset=utf-8," + escape(html_string); // alert data:text/html;charset=utf-8..... // access cookie get ERROR var doc = document.getElementById('iframe').contentWindow.document; doc.open(); doc.write('<body>Test<script>alert(location.href);</script></body>'); doc.close(); // alert "top window url" var iframe = document.createElement('iframe'); iframe.src = 'javascript:;\'' + encodeURI('<html><body><script>alert(location.href);</body></html>') + '\''; // iframe.src = 'javascript:;"' + encodeURI((html_tag).replace(/\"/g, '\\\"')) + '"'; document.body.appendChild(iframe); // alert "about:blank" </script>
在这种状况下,您的服务器须要发送JavaScript response并让客户端浏览器执行它。仅包括JS脚本连接。
(function () { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = '/yourscript?some=parameter&callback=jsonpCallback'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })();
要了解有关jsonp的更多信息
查看 documentation。
此方法解决了分析和诊断代码的需求,这些代码一般在卸载文档以前尝试将数据发送到Web服务器。尽快发送数据可能会致使错过收集数据的机会。可是,确保数据在文档卸载期间已发送是开发人员传统上难以作到的事情。
经过API发送POST beacon。这个很酷。
navigator.sendBeacon("/log", analyticsData);
编写XMLHttpRequest不是一个好主意。我假设您不想浪费时间与IE或其余浏览器做战。如下是一些您能够尝试使用的polyfill或代码:
<ol>
<li>window.fetch - A window.fetch JavaScript polyfill. (check also ky)</li>
<li>got - Simplified HTTP/HTTPS requests</li>
<li>microjs - list of ajax lib</li>
<li>more</li>
</ol>
请记住,结尾带有哈希标记的请求不会在http请求中传递。
例如,您在页面中 http://github.com/awesome#hueitan
// Sending a request with a parameter url which contains current url (new Image()).src = 'http://yourrequest.com?url=http://github.com/awesome#hueitan'; // actual request will be without # (new Image()).src = 'http://yourrequest.com?url=http://github.com/awesome'; // Solution, encodeURIComponent(url): (new Image()).src = 'http://yourrequest.com?url=' + encodeURIComponent('http://github.com/awesome#hueitan');
检查浏览器请求链接的最大数量。browserscope
重要的是要知道SDK是否须要解析位置网址。
authority __________|_________ / \ userinfo host resource __|___ ___|___ __________|___________ / \ / \ / \ username password hostname port path & segment query fragment __|___ __|__ ______|______ | __________|_________ ____|____ | / \ / \ / \ / \ / \ / \ / \ foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo \_/ \ / \ \ / \__________/ \ \__/ | | \ | | \ | scheme subdomain \ tld directory \ suffix \____/ \___/ | | domain filename
这是使用本机URL()接口的简单方法,但并不是全部浏览器都支持。它也不是一个标准。
var parser = new URL('http://github.com/hueitan'); parser.hostname; // => "github.com"
The DOM 'screateElement('a')
can be used in browsers that don't have theURL()
Interface yet.
var parser = document.createElement('a'); parser.href = "http://github.com/hueitan"; parser.hostname; // => "github.com"
要模拟多个域,无需注册其余域名。编辑操做系统的主机文件能够解决这个问题。
$ sudo vim / etc / hosts
添加如下条目
# refer to localhost 127.0.0.1 publisher.net 127.0.0.1 sdk.net
每一个网站的网址均可以经过http://publisher.net
和http://sdk.net
访问
浏览器带有针对每一个供应商的调试工具。显然,这些工具可用于调试SDK JavaScript代码- Chrome Developer Tools
Safari Developer Tools
Firebug
。开发人员工具也简称为DevTools。
DevTools为Web开发人员提供了对浏览器及其Web应用程序内部的深刻访问。使用DevTools能够有效地跟踪布局问题,设置JavaScript断点并得到有关代码优化的看法。
为了测试预期的输出文本和其余常规调试,Console Logs能够经过浏览器API使用console.log()。格式化和输出消息有多种类型。在此连接上讨论了更多有关此内容:Console API。
调试代理使咱们能够在开发中测试SDK。涉及的领域包括:
这是您能够尝试的一些软件
经过同步文件更改和跨多个设备的交互,BrowserSync使调整和测试更快变得容易。它速度快,彻底免费。
跨多种设备测试SDK确实颇有帮助。彻底值得一试=)
在Chrome开发者工具中调试SDK脚本。(须要Node.js v6.3.0 +)
$ node --inspect-brk [script.js]
在某些状况下,有时不须要包括全部SDK源代码。这是一个简单的1x1像素请求的状况-例如:当有人访问“谢谢”(最后)页面时发出请求。在这种状况下,开发人员能够包括具备(url)连接的图像文件,如如下代码段所述。
<img height="1" width="1" alt="" style="display:none" src="https://yourUrlLink.com/t?timestamp=1234567890&type=page1¤cy=USD&noscript=1" />
有时,SDK但愿检测用户是否关注特定页面。这些polyfills visibly.js和visibilityjs能够帮助实现这一点。
document.referrer
可用于获取当前或前一页面的URL。可是,建议记住该引荐来源网址为“浏览器引荐来源网址”,而不是“人类已知引荐来源网址”。用户单击浏览器后退按钮(例如pageA-> pageB-> pageC->(后退按钮)pageB)的状况下,当前pageB的引荐来源网址是pageA,而不是pageC。
如下不是特殊的polyfill。它只是确保调用console.logAPI不会向客户端抛出错误事件。
if (typeof console === "undefined") { var f = function() {}; console = { log: f, debug: f, error: f, info: f }; }
了解escape()
encodeURI()
encodeURIComponent()
here。
值得一提的是,使用encodeURI()
和encodeURIComponent()
有11个字符的不一样。这些字符是:#$&+,/:; =?@ more discussion。
如标题所述,您可能不须要jquery。若是您正在寻找一些实用程序代码-AJAX EFFECTS, ELEMENTS, EVENTS, UTILS,这真的颇有用
经过拥抱和理解现代Web API并发现各类有向库来帮助您填补空白,从jQuery的链中解放本身。
http://blog.garstasio.com/you-dont-need-jquery/
有用的提示
这相似于带有附加回调事件的异步脚本加载
function loadScript(url, callback) { var script = document.createElement('script'); script.async = true; script.src = url; var entry = document.getElementsByTagName('script')[0]; entry.parentNode.insertBefore(script, entry); script.onload = script.onreadystatechange = function () { var rdyState = script.readyState; if (!rdyState || /complete|loaded/.test(script.readyState)) { callback(); // detach the event handler to avoid memory leaks in IE (http://mng.bz/W8fx) script.onload = null; script.onreadystatechange = null; } }; }
功能的实现 once
一般,有些功能只须要运行一次便可。这些功能一般以事件侦听器的形式出现,可能难以管理。固然,若是它们易于管理,建议删除监听器。如下是使之成为可能的JavaScript函数!
// Copy from DWB // http://davidwalsh.name/javascript-once function once(fn, context) { var result; return function() { if(fn) { result = fn.apply(context || this, arguments); fn = null; } return result; }; } // Usage var canOnlyFireOnce = once(function() { console.log('Fired!'); }); canOnlyFireOnce(); // "Fired!" canOnlyFireOnce(); // nada. nothing.
为了在开发移动网络时更好地理解像素,比率,密度,尺寸等术语,如下连接能够提供更多看法:
得到内联样式的价值
<span id="black" style="color: black"> This is black color span </span> <script> document.getElementById('black').style.color; // => black </script>
得到真实风格的价值
<style> #black { color: red !important; } </style> <span id="black" style="color: black"> This is black color span </span> <script> document.getElementById('black').style.color; // => black // real var black = document.getElementById('black'); window.getComputedStyle(black, null).getPropertyValue('color'); // => rgb(255, 0, 0) </script>
参考:https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
还有更多的在这里。
函数 isElementInViewport(el){
function isElementInViewport (el) { //special bonus for those using jQuery if (typeof jQuery === "function" && el instanceof jQuery) { el = el[0]; } var rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */ rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */ ); }
var isVisible = function(b) { var a = window.getComputedStyle(b); return 0 === a.getPropertyValue("opacity") || "none" === a.getPropertyValue("display") || "hidden" === a.getPropertyValue("visibility") || 0 === parseInt(b.style.opacity, 10) || "none" === b.style.display || "hidden" === b.style.visibility ? false : true; } var element = document.getElementById('box'); isVisible(element); // => false or true Get Viewport Size
var getViewportSize = function() { try { var doc = top.document.documentElement , g = (e = top.document.body) && top.document.clientWidth && top.document.clientHeight; } catch (e) { var doc = document.documentElement , g = (e = document.body) && document.clientWidth && document.clientHeight; } var vp = []; doc && doc.clientWidth && doc.clientHeight && ("CSS1Compat" === document.compatMode || !g) ? vp = [doc.clientWidth, doc.clientHeight] : g && (vp = [doc.clientWidth, doc.clientHeight]); return vp; } // return as array [viewport_width, viewport_height]
假设Evil广告公司想要跟踪用户,Evil能够经过使用指纹很好地生成个性化的惟一hash。可是,Evil公司使用Cookie并提供Opt out解决方案。
Opt-out
DIGITAL ADVERTISING ALLIANCE, POWERED BY YOURADCHOICES提供支持的数字广告联盟提供了一种工具,能够帮助任何人从全部参与公司opt-out。
为何HTTP请求头具备字段名称有趣的事实referer
不referrer
根据维基百科
它misspelling of referrer
起源于计算机科学家Phillip Hallam-Baker提出的将该领域归入HTTP规范的原始建议。在将拼写错误归入Request for Comments
标准文件RFC 1945
时已陷入僵局; 该文档的合著者Roy Fielding指出,Unix spell checker这段时间的标准既不承认“Referer”,也不拼写错误的“referer” 。此后,在讨论HTTP引荐来源网址时,“Referer”已成为业界普遍使用的拼写形式;不过,拼写错误的用法并不广泛,由于在某些网络规范(例如 Document Object Model)中使用了正确的拼写“referrer” 。
本指南提供了用于构建SDK的模板和样板。
Third-Party JavaScript
JQuery Plugin
LightningJS
(灵感来自_http-api-design_)