本指南提供了開發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_)