因爲篇幅過於長,知識體系過於龐雜,請謹慎學習, 轉述的我都有放置連接(難理解的也放了demo測試或者圖)javascript
技術規範(基礎瞭解,大佬儘可跳過)php
ES5/ES6等歷程瞭解 jscss
tips: browser object model(即瀏覽器對象模型,核心是window)html
BOM是一系列在瀏覽器環境中使用對象的統稱,這些對象提供了訪問瀏覽器的功能。前端
通俗講就是JavaScript訪問瀏覽器的一個接口 ECMAScript規定的Global對象(全局對象)java
介紹:咱們定義的全局變量和全局函數都是window對象的屬性和方法node
MDN全window文檔,能夠查閱jquery
查閱資料git
查閱資料 思否 JavaScript BOM對象-Navigator瀏覽器對象程序員
參考JS高級程序設計-第3版,挺好的書(有須要找我要電子書)
深刻學習History對象管理瀏覽器會話歷史 BOM之history對象 history對象 前端離開頁面事件總結
navigator對象:包含大量有關Web瀏覽器的信息,在檢測瀏覽器及操做系統上很是有用(window.navigator或者navigator) 每一個瀏覽器中的 navigator 對象也都有一套本身的屬性
基本大概有須要的
//如下也能夠經過window.navigator獲取到
navigator.appCodeName //瀏覽器代碼名的字符串表示
navigator.appName //官方瀏覽器名的字符串表示
navigator.appVersion //瀏覽器版本信息的字符串表示
navigator.cookieEnabled //若是啓用cookie返回true,不然返回false
navigator.javaEnabled //若是啓用java返回true,不然返回false
navigator.platform //瀏覽器所在計算機平臺的字符串表示
navigator.plugins //安裝在瀏覽器中的插件數組
navigator.taintEnabled //若是啓用了數據污點返回true,不然返回false
navigator.userAgent //用戶代理頭的字符串表示
Navigator.onLine //返回一個布爾值,表示用戶當前在線仍是離線(瀏覽器斷線)
複製代碼
var UserAgent =window.navigator.userAgent.toLowerCase();
function getBrowserType(UserAgent){
var result={
isIE6: /msie 6.0/.test(UserAgent), // IE6
isIE7: /msie 7.0/.test(UserAgent), // IE7
isIE8: /msie 8.0/.test(UserAgent), // IE8
isIE9: /msie 9.0/.test(UserAgent), // IE9
isIE10: /msie 10.0/.test(UserAgent), // IE10
isIE11: /msie 11.0/.test(UserAgent), // IE11
isLB: /lbbrowser/.test(UserAgent), // 獵豹瀏覽器
isUc: /ucweb/.test(UserAgent), // UC瀏覽器
is360: /360se/.test(UserAgent), // 360瀏覽器
isBaidu: /bidubrowser/.test(UserAgent), // 百度瀏覽
isSougou: /metasr/.test(UserAgent), // 搜狗瀏覽器
isChrome: /chrome/.test(UserAgent),
//Chrome瀏覽器
isFirefox: /firefox/.test(UserAgent), // 火狐瀏覽器
isOpera: /opera/.test(UserAgent), // Opera瀏覽器
isSafiri: /safari/.test(UserAgent) && !/chrome/.test
(UserAgent), // safire瀏覽器
isQQ: /qqbrowser/.test(UserAgent)//qq瀏覽器
};
return result;
}
console.log(getBrowserType(UserAgent));
複製代碼
只用來代表客戶端的能力,包括瀏覽器窗口外部的顯示器的信息,如像素高度和寬度等。每一個瀏覽器中的screen對象都包含着各不相同的屬性
各大瀏覽器實現的屬性
ps:說起兼容性高的
屬性 | 說明 | IE | FireFox | Safari/Chrome | Opera |
---|---|---|---|---|---|
availHeight | 返回顯示屏幕的高度 (除 Windows 任務欄以外) | ✔ | ✔ | ✔ | ✔ |
availWidth | 返回顯示屏幕的寬度 (除 Windows 任務欄以外) | ✔ | ✔ | ✔ | ✔ |
width | 返回顯示器屏幕的寬度 | ✔ | ✔ | ✔ | ✔ |
height | 返回顯示屏幕的高度 | ✔ | ✔ | ✔ | ✔ |
colorDepth | 返回目標設備或緩衝器上調色板的比特深度(多數系統32位) | ✔ | ✔ | ✔ | ✔ |
pixelDepth | 返回顯示屏幕的顏色分辨率(比特每像素) | × | ✔ | ✔ | ✔ |
availLeft | 返回未被系統部件佔用的最左側的像素 | × | ✔ | ✔ | × |
availTop | 返回未被系統部件佔用的最上方的像素 | × | ✔ | ✔ | × |
History對象容許咱們操做瀏覽器會話歷史,即加載當前頁面的標籤頁窗口或frame窗口的訪問歷史
屬性(window.history獲取)
history.length:包括當前頁面在內的會話歷史中的記錄數量
history.state:屬性用來保存記錄對象(返回當前頁面的state對象,上述的state對象)【區別不一樣會話歷史紀錄】
導航方法(window.history獲取)
history.back():返回會話歷史記錄中的上一個頁面
history.forward():進入會話歷史記錄中的下一個頁面
history.go():加載會話歷史記錄中的某一個頁面,該頁面與當前頁面在會話歷史中的相對位置定位,-1表明當前頁面的上一個記錄,1表明當前頁面的下一個頁面
增改記錄
方法
history.pushState():在瀏覽歷史中添加【向瀏覽器歷史添加了一個狀態】
state —— 狀態對象是一個由pushState()方法建立的、與歷史紀錄相關的javascript對象,當用戶定向到一個新的狀態時,會觸發popstate事件
title —— 新頁面的標題,可是全部瀏覽器目前都忽略這個值,所以這裏能夠填null
url —— 必須與當前頁面URL同源
語法: history.pushState(state, title, url);
var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");
複製代碼
history.replaceState():在瀏覽歷史中修改記錄(更新當前會話歷史記錄,如更新當前會話記錄的狀態對象或URL。)
事件
popstate:監聽history對象的變化【每當同一個文檔的瀏覽歷史(即history對象)出現變化時,就會觸發popstate事件】
僅僅調用pushState方法或replaceState方法,並不會觸發該事件,只有用戶點擊瀏覽器倒退按鈕和前進按鈕,或者使用javascript調用back()、forward()、go()方法時纔會觸發
往返緩存概念(瀏覽器有一個特性)
能夠在用戶使用瀏覽器的「後退」和「前進」按鈕時加快頁面的轉換速度
這個緩存中不只保存着頁面數據,還保存了DOM和javascript的狀態
若是頁面位於bfcache中,那麼再次打開該頁面時就不會觸發load事件
進入頁面與離開頁面事件 pageshow與 pagehide=》(沒法執行alert操做,window 對象 和 dom 對象都已經被銷燬了)
pageshow:頁面加載時觸發,包括第一次加載和從緩存加載兩種狀況 ps:第一次加載時,它的觸發順序排在load事件後面
window.onpageshow = function(e){
e = e || event;
console.log(e.persisted,進入加載事件);
}
複製代碼
pagehide:該事件會在瀏覽器卸載頁面的時候觸發,並且是在unload事件以前觸發
window.onpagehide = function(e){
e = e || event;
console.log(e.persisted);
}
複製代碼
前端離開頁面事件總結(出現提示框有點會有點問題,由於alert是阻塞)
Onblur :監控(窗口 頁面 組件)失去焦點事件
Onfocus :監控(窗口 頁面 組件)獲得焦點事件
onbeforeunload:在即將離開當前頁面(刷新或關閉)時執行 JavaScript
window.onbeforeunload=function(e){
  var e = window.event||e;
  e.returnValue=("肯定離開當前頁面嗎?");
}
複製代碼
以http://www.baidu.com:8080/index.php?name=kang&when=2011#first爲例
屬性 | 描述 | 值 |
---|---|---|
protocol | 設置或返回當前 URL 的協議。 | http: |
hostname | 設置或返回當前 URL 的主機名。 | www.baidu.com |
host | 設置或返回主機名和當前 URL 的端口號 | www.baidu.com:8080 |
port | 返回當前 URL 的端口號 | 8080 |
pathname | 設置或返回當前 URL 的路徑部分 | /index.php |
search | 設置或返回從問號 (?) 開始的 URL(查詢部分) | ?name=kang&when=2011 |
hash | 設置或返回從井號 (#) 開始的 URL(錨) | #first |
href | 設置或返回完整的 URL | 全連接 |
window.location和document.location互相等價。 location 的8個屬性都是可讀寫,可是隻有href與hash的寫纔有意義
方法
assign() :加載新的文檔
導航到一個新頁面的方法
window.location.assign("http://www.mozilla.org"); // or
window.location = "http://www.mozilla.org";
複製代碼
reload() :從新加載當前文檔
若是文檔已改變,reload() 會再次下載該文檔。若是文檔未改變,則該方法將從緩存中裝載文檔。這與用戶單擊瀏覽器的刷新按鈕的效果是徹底同樣的。若是把該方法的參數設置爲 true,那麼不管文檔的最後修改日期是什麼,它都會繞過緩存,從服務器上從新下載該文檔
window.location.reload(true);
複製代碼
不會在 History 對象中生成一個新的記錄。當使用該方法時,新的 URL 將覆蓋 History 對象中的當前記錄。
window.location.replace('http://example.com/');
複製代碼
兼容問題
document.documentElement.body || document.body 區別 document.body 和 document.documentElement 的區別
獲取 scrollTop 方面的差別
兼容性解決方案:var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
DTD概念普及 W3C HTML中 !DOCTYPE 的解釋與做用
Document Type Definition,中文翻譯爲:文檔類型定義【整體上應該分爲三類: HTML5,HTML4.0,XHTML。】
早期的版本基於SGML,因此須要套用SGML的解析規則。DTD的做用在於定義SGML文檔的文檔類型以便於瀏覽器解析。
隨着技術的進步,如今HTML5 不基於 SGML,因此也就不須要引用 DTD了
牽扯處一個問題什麼是!DOCTYPE,以及做用(面試題)
DOCTYPE是document type(文檔類型)的簡寫,在Web設計中用來講明你用的XHTML或者HTML是什麼版本
沒有<!DOCTYPE>聲明,那麼不一樣的瀏覽器將會以本身不一樣的怪異的模式去解析渲染頁面,這樣頁面在不一樣的瀏覽器上呈現出來的效果也就不同,人們把這稱之爲「怪異模式」
若是聲明瞭,將會開啓「嚴格模式」,又有人稱之爲「標準模式」,瀏覽器將已w3c標準來解析渲染頁面
document獲取到當前頁面不少數據信息【屬性】
全部圖片images/表單forms/連接links/錨點anchors的集合
document.images/document.forms/document.links/document.anchors
複製代碼
當前頁的cookie/域名domain/獲取載入當前文檔的文檔的url/標題title/當前url
document.cookie/document.domain/document.referrer/document.title/document.URL
複製代碼
方法
不要和 window.open() 方法混淆。document.open 可用於重寫當前的文檔內容或者追加內容, 而 window.open 是提供了打開一個新的窗口的方法,當前的網頁文檔內容會被保留
window.scrollBy(x,y)/window.scrollTo(x,y)挺有用的,移動文檔
MoveTo和MoveBy的區別(可引伸爲To和By的區別)
By 和 To 的區別(move/resize/scroll)--窗口移動變化(瀏覽器禁止)/窗口大小變化(瀏覽器可能會禁止掉)/內容滾動
By 算的是相對於節點對象的當前位置
To 算的是絕對位置,不考慮當前節點對象在哪
moveBy()/moveTo()----->在chrome,IE瀏覽器上運行,都不顯示效果
緣由:爲了保護計算機的安全性
----語法很少贅述
javascript都是以單線程的方式運行於瀏覽器的javascript引擎中的 settimeout和setinterval的做用只是把你要執行的代碼在你設定的一個時間點插入js引擎維護的一個代碼隊列中 插入代碼隊列並不意味着你的代碼就會立馬執行的,理解這一點很重要 settimeout和setinterval還有點不同
扯出最最重要的知識點:Event Loop(面試題) 通殺 Event Loop 面試題 / Js 的事件循環(Event Loop)機制以及實例講解
js是單線程的腳本語言,在同一時間,只能作同一件事,爲了協調事件、用戶交互、腳本、UI渲染和網絡處理等行爲,防止主線程阻塞,Event Loop方案應運而生
知識點的普及
執行棧
當執行某個函數、用戶點擊一次鼠標,Ajax完成,一個圖片加載完成等事件發生時,只要指定過回調函數,這些事件發生時就會進入任務隊列中,等待主線程讀取,遵循先進先出原則
主線程
任務隊列(Task Queue)
順序講解: 當主線程將執行棧中全部的代碼執行完以後,主線程將會去查看任務隊列是否有任務。若是有,那麼主線程會依次執行那些任務隊列中的回調函數。
宏任務與微任務(很重要)
異步任務分爲 宏任務(macrotask) 與 微任務 (microtask),不一樣的API註冊的任務會依次進入自身對應的隊列中,而後等待 Event Loop 將它們依次壓入執行棧中執行。
宏任務(如下都是) script(總體代碼)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 環境)、UI rendering (瀏覽器獨有)
微任務(如下都是) Promise.then()、 MutaionObserver、process.nextTick(Node.js環境)、Object.observe
Event Loop(事件循環) 【全部任務當作兩個隊列:執行隊列與事件隊列】
--->先執行script --->遇到宏任務(放入事件列表),遇到微任務(先依次壓入執行棧),等script裏的同步任務完成 --->再執行執行棧裏面的微任務 --->最後執行事件列表裏的宏任務
注意:new Promise() 構造函數裏面是同步代碼,而非微任務
promise.then 雖然和 process.nextTick 同樣,都將回調函數註冊到微任務,但優先級不同。process.nextTick 的微任務隊列 老是優先於 promise 的 微任務隊列 執行
在 I/O Callbacks 中註冊的 setTimeout 和 setImmediate,永遠都是 setImmediate 先執行。
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
console.log(4);
1. 遇到setTimeout(宏任務)壓到事件列表
2. 進入Promise,遇到console.log(2),打印2
3. 遇到resolve(3),回調函數【Promise.then是微任務】壓入執行棧
3. 遇到console.log(4),執行4-------》執行完script
4. 執行微任務resolve(3),轉到then,打印3
4. 最後執行事件列表,執行宏任務setTimeout,打印1
複製代碼
--》 2 4 3 1
鞏固提升
console.time("start")
setTimeout(function () {
console.log(2);
}, 10);
setImmediate(function () {
console.log(1);
});
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
console.timeEnd("start")
});
console.log(6);
process.nextTick(function () {
console.log(7);
});
console.log(8);
// requestAnimationFrame(() => console.log(9))。
複製代碼
講完了這個執行順序(賊重要)問題,回到計時器
關於setTimeout()你所不知道的地方,詳解setTimeout()
將指定的代碼移出本次執行,等到下一輪Event Loop時,再檢查是否到了指定時間。若是到了,就執行對應的代碼;若是不到,就等到再下一輪Event Loop時從新判斷。這意味着, setTimeout和setInterval指定的代碼,必須等到本次執行的全部代碼都執行完,纔會執行
因爲前面的任務到底須要多少時間執行完,是不肯定的,因此沒有辦法保證,setTimeout和setInterval指定的任務,可能會不必定按照預約時間執行
setTimeout來代替setInterval
timer=setTimeout(fn,1000)
function fn(){
console.log("循環")
//argement.callee表明表明上下文正在執行的函數--》這些都會在後面一一細說
setTimeout(arguments.callee,1000)
}
複製代碼
setTimeout(fn,0)來置換順序
setTimeout(fn,time),在執行過程當中time是能改變的(調整快慢)
setTimeout循序回調(傳參)
setTimeout(function(a,b){
console.log(a+b);
},1000,1,1);
複製代碼
注意點:setTimeout()中回調函數中的this(會指向全局,利用apply/call解決) 你所不知道的setTimeout
爲何不使用 setInterval-->跳幀---》解決方案,好比容易內存泄漏 深度解密setTimeout和setInterval——爲setInterval正名 / 從setTimeout-setInterval看JS線程(setInterval缺點) / 阮一峯的線程與進程
alert :顯示警告框的信息; 無返回值
confirm:參數就只有一個.顯示提示框的信息. 按肯定,返回true; 按取消返回false.
var truthBeTold = window.confirm("單擊「肯定」繼續。單擊「取消」中止。");
if (truthBeTold) {
window.alert("歡迎訪問咱們的 Web 頁!");
} else {
window.alert("再見啦!");
}
複製代碼
prompt:參數,有兩個, 第一個參數,顯示提示輸入框的信息. 第二個參數,用於顯示輸入框的默認值. 返回,用戶輸入的值.
var data = window.prompt("輸入驗證碼", "")
if( data == 'ss' )
{
alert("ok");
}
else
{
alert("false");
}
複製代碼
JavaScript中的各類寬高屬性 / JS 相關寬高理解,有圖表能夠查閱
瞭解跨瀏覽器肯定窗口大小是很不容易的:
window.innerHeight/innerWidth:第一種方式獲取的是可視區的寬高,包括了滾動條的寬度
ps:IE 8 及更早 IE版本不支持這兩個屬性。
document.body.clientHeight/clientWidth: 第二種方式獲取的是可視區的寬高,不包括滾動條的寬度以及邊框border【 獲取body寬度】
兼容性寫法:document.documentElement.clientHeight/clientWidth(獲取整個頁面)
document.documentElement對象的clientWidth..... / 如何使用 JavaScript 獲取頁面、窗口的高度?
// 頁面可視區的寬度
var oClientWidth = document.documentElement.clientWidth || document.body.clientWidth || 0;
// 頁面可視區的高度
var oClientHeight = document.documentElement.clientHeight || document.body.clientHeight || 0;
複製代碼
document.body.offsetWidth/offsetHeight :是可視區的寬高(即元素佔據的空間,包括填充和邊框,不包括滾動條)【 獲取body寬高度】
兼容性寫法:document.documentElement.offsetWidth/offsetHeight(獲取整個頁面)
// scrollWidth 表示整個網頁正文的寬度
var scrollWidth = document.documentElement.offsetWidth || document.body.offsetHeight;
// scrollHeight 表示整個網頁正文的高度
var scrollHeight = document.documentElement.offsetWidth || document.body.offsetHeight;
複製代碼
document.body.scrollWidth/scrollHeight: 網頁正文全文寬高【 獲取body正文全文寬高】
兼容性寫法:document.documentElement.scrollWidth/scrollHeight(獲取整個頁面正文全文寬高)
// scrollWidth 表示整個網頁正文的寬度
var scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
// scrollHeight 表示整個網頁正文的高度
var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
複製代碼
頁面正文捲起長度
解決兼容:scrollTop/scrollLeft
// scrollTop 網頁被捲起的高度
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
// scrollLeft 網頁左邊被捲起的寬度
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
複製代碼
window 相關寬高
Event對象的5種座標
clientX和clientY,相對於瀏覽器(但是區左上角0,0)的座標
screenX和screenY,相對於設備屏幕左上角(0,0)的座標
offsetX和offsetY,相對於事件源左上角(0,0)的座標
pageX和pageY,相對於整個網頁左上角(0,0)的座標
X和Y,原本是IE屬性,相對於用CSS動態定位的最內層包容元素
關於DOM級別的一些問題 DOM事件全整理之從DOM事件級別,DOM事件流到事件委託
文檔對象模型 (DOM) 是HTML和XML文檔的編程接口。它提供了對文檔的結構化的表述,並定義了一種方式可使從程序中對該結構進行訪問,從而改變文檔的結構,樣式和內容。
DOM描繪了一個層次化的節點樹,容許開發人員添加、移除和修改頁面的某一部分。
簡單來說,DOM就是一組API(接口)。它將一份結構化文檔看作一棵樹,這棵樹由各類各樣的節點構成,也即節點樹
精闢:DOM標準的目標是讓「任何一種程序設計語言」能操控使用「任何一種標記語言」編寫出的「任何一份文檔」。「操控」具體含義爲能經過DOM提供的API對文檔的內容、結構、樣式進行訪問和修改
方法
getElementById():根據元素的id來獲取該元素對象,經過該方法獲取的元素對象是惟一的,能夠直接對其進行操做
getElementsByClassName():根據元素的class類名來獲取該元素對象,經過該方法獲取的元素對象是一個僞數組,須要經過僞數組的方式對其進行訪問
getElementsByTagName():根據元素的標籤名來獲取該元素對象,經過該方法獲取的元素對象是一個僞數組,須要經過僞數組的方式對其進行訪問
是 HTML5 新增長的一個 js 函數,對於一些老瀏覽器例如 IE9 如下的瀏覽器是不支持這個函數的,因此在那些須要處理兼容性問題的
getElementsByClassName()方法不兼容ie低版本解決方案
if(!document.getElementsByClassName)//判斷瀏覽器是否支持這個方法
{
document.getElementsByClassName=function(cname){
var selected=new Array();
var alltag=document.getElementsByTagName("*");//獲取全部標籤
for(var i=0;i<alltag.length;i++)
{
var t=alltag[i];
alert(t.className);
if(t.className==cname) //比較標籤的class與所要查找的class是否相同
{
selected.push(t); //將相同的存入數組
}
}
return selected;
}
}
複製代碼
querySelector與getElement...差別以及區別 querySelector和getElementById性能分析與使用選擇 / 爲什麼getElementsByTagName()比querySelectorAll()快100倍
document.querySelector():返回文檔中匹配指定的CSS選擇器的第一元素
document.querySelector("#demo");
document.querySelectorAll():是 HTML5中引入的新方法,返回文檔中匹配的CSS選擇器的全部元素節點列表
var x = document.querySelectorAll(".example");
認識DOM文檔的遍歷指針以及如何獲取根節點和body節點 / 《JavaScript高級程序設計》學習筆記(四)
知識點普及:Node類型
DOM1級定義了一個Node接口,該接口由DOM中的全部節點類型實現。這個Node接口在JavaScript中是做爲Node類型實現。除了IE以外,在其餘瀏覽器中均可以訪問到這個類型。JavaScript中全部節點類型都繼承自Node類型,所以全部節點類型都共享着相同的基本屬性和方法
在DOM結構樹中,每個獨立的DOM節點都定義了一系列的指針(節點屬性),經過這些指針,咱們能夠遍歷DOM結構中咱們想找到的任何對象。若是指針指向的元素不存在,則對應指針的屬性值爲null
nodeName和nodeValue屬性-------》【擴展到js原生事件委託機制(事件的捕獲冒泡)】
用nodeName和nodeValue屬性能夠了解節點的具體信息,其值徹底取決於節點的類型 nodeName中保存的始終都是元素的標籤名,而nodeValue的值則始終爲null
Javascript性能優化 - 事件委託 / JavaScript 事件委託詳解 / 事件冒泡機制與委託機制 / JavaScript事件委託原理 / JavaScript事件代理和委託(Delegation)
事件委託機制
場景:
ps:假設有一個多行多列的表格,咱們想讓用戶單擊每一個單元格都能看到與其中內容相關的更多信息(好比,經過提示條)。爲此,能夠爲每一個單元格都綁定click事件.問題是,若是表格中要綁定單擊事件的有10列500行,那麼查找和遍歷5000個單元格會致使腳本執行速度明顯變慢,而保存5000個td元素和相應的事件處理程序也會佔用大量內存
ps:假設一個動態翻頁的表格,每一次翻頁都是從新插入的表格。經過給第一頁添加事件,對後續經過js異步加載的頁面沒有做用
提示:使用事件委託時,若是註冊到目標元素上的其餘事件處理程序使用.stopPropagation()阻止了事件傳播,那麼事件委託就會失效。
事件觸發過程:現代瀏覽器(指 IE6-IE8 除外的瀏覽器,包括 IE9+、FireFox、Safari、Chrome 和 Opera 等)事件流包含三個過程,分別是捕獲階段、目標階段和冒泡階段 JavaScript 瀏覽器事件解讀
當咱們對 DOM 元素進行操做時,好比鼠標點擊、懸浮等,就會有一個事件傳輸到這個 DOM 元素,這個事件從 Window 開始,依次通過 docuemnt、html、body,再不斷通過子節點直到到達目標元素,從 Window 到達目標元素父節點的過程稱爲捕獲階段,注意此時還未到達目標節點。
捕獲階段結束時,事件到達了目標節點的父節點,最終到達目標節點,並在目標節點上觸發了這個事件,這就是目標階段。
當事件到達目標節點以後,就會沿着原路返回,這個過程有點相似水泡從水底浮出水面的過程,因此稱這個過程爲冒泡階段。
addEventListener(eventName, handler, useCapture) 函數。第三個參數是 useCapture,表明是否在捕獲階段進行事件處理, 若是是 false, 則在冒泡階段進行事件處理,若是是 true,在捕獲階段進行事件處理,默認是 false。這麼設計的主要緣由是當年微軟和 netscape 之間的瀏覽器戰爭打得火熱,netscape 主張捕獲方式,微軟主張冒泡方式,W3C 採用了折中的方式,即先捕獲再冒泡。(true 時表捕獲階段,false時爲冒泡階段)
main.addEventListener("mousedown",function(e){ //e.target是鼠標通過的那個元素
var nodename = e.target.nodeName.toLocaleLowerCase(); //標籤轉小寫
var classname=e.target.className; //獲取class
if(e.target && nodename == "div" || nodename=="span") {
if(classname=="head"||classname=="msg"||classname=="foot"||classname=="tip"|| nodename=="span"){
if(classname=="tip"){
...........
}else if(nodename=="span"){
........
}else{
............
}
}
}
},true);
複製代碼
事件委託是事件冒泡的一個應用,但有時候咱們並不但願事件冒泡
event.stopPropagation():阻止冒泡事件,不讓事件向documen上蔓延,可是默認事件任然會執行,當你掉這個方法的時候,若是點擊一個連接,這個連接仍然會被打開;
ps:但IE不支持,IE中提供的是,cancelBubble屬性,默認爲false,當它設置爲true時,就是阻止事件冒泡
event.preventDefault():阻止默認事件,調用此方法是,連接不會被打開,可是會發生冒泡,冒泡會傳遞到上一層的父元素;
return false:這個方法比較暴力,他會同時阻止事件冒泡也會阻止默認事件;寫上此代碼,連接不會被打開,事件也不會傳遞到上一層的父元素,能夠理解爲 return false 就等於同時調用了event.stopPropagation()和event.preventDefault()。
講到冒泡=>其實能夠扯到onmouseover/onmouseout 以及onmouseenter/onmouseleave
事件冒泡以及onmouseenter 和 onmouseover 的不一樣
冒泡 onmouseover/onmouseout是有冒泡的
onmouseenter/onmouseleave是沒有冒泡的
mouseover
mouseout
Node(節點)對應的5個指針
parentNode:返回指定節點的父節點
previousSibling:返回指定節點的上一個相鄰節點
nextSibling:返回指定節點的下一個相鄰節點
firstChild:返回指定元素的第一個子節點
lastChild:返回指定元素的最後一個子節點
childNodes
test.firstChild=test.childNodes[0] 始終相等
test.lastChild=test.[test.childNodes.length-1] 始終相等
在只有一個子節點的狀況下,firstChild和lastChild指向同一個節點,若是沒有子節點,firstChild和lastChild的值均爲null。
JavaScript常見原生DOM操做API總結 JavaScript操做DOM經常使用的API
建立節點
createElement:傳入指定的一個標籤名來建立一個元素
let elem = document.createElement("div");
elem.id = 'test';
elem.style = 'color: red';
elem.innerHTML = '我是新建立的節點';
document.body.appendChild(elem);
複製代碼
createTextNode:建立一個文本節點
var node = document.createTextNode("我是文本節點");
document.body.appendChild(node);
複製代碼
cloneNode:返回調用該方法的節點的一個副本
是否採用深度克隆,若是爲true,則該節點的全部後代節點也都會被克隆,若是爲false,則只克隆該節點自己
要調用appendChild方法才能添加到文檔樹中
若是複製的元素有id,則其副本一樣會包含該id,因爲id具備惟一性,因此在複製節點後必需要修改其id
調用接收的是否深度克隆參數最好傳入,若是不傳入該參數,不一樣瀏覽器對其默認值的處理可能不一樣
須要克隆的元素,事件都啓用,最好使用事件委託機制
var parent = document.getElementById("parent");
document.getElementById("btnCopy").onclick = function(){
var parent2 = parent.cloneNode(true);
parent2.id = "parent2";
document.body.appendChild(parent2);
}
複製代碼
createDocumentFragment:建立一個 DocumentFragment ,也就是文檔碎片,它表示一種輕量級的文檔,主要是用來存儲臨時節點,大量操做DOM時用它能夠大大提高性能。
(function()
{
var start = Date.now();
var str = '', li;
var ul = document.getElementById('ul');
var fragment = document.createDocumentFragment();
for(var i=0; i<1000; i++)
{
li = document.createElement('li');
li.textContent = '第'+(i+1)+'個子節點';
fragment.appendChild(li);
}
ul.appendChild(fragment);
})();
複製代碼
插入節點
appendChild:前面已經用到屢次,就是將指定的節點添加到調用該方法的節點的子元素的末尾。
insertBefore(newElement, referenceElement):用來添加一個節點到一個參照節點(referenceElement)以前
<div id="parent">
父節點
<div id="child">
子元素
</div>
</div>
<input type="button" id="insertNode" value="插入節點" />
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").onclick = function(){
var newNode = document.createElement("div");
newNode.textContent = "新節點"
parent.insertBefore(newNode,child);
}
複製代碼
替換節點 :
parent.replaceChild(newChild,oldChild)用於使用一個節點替換另外一個節點【oldChild是被替換的節點】
<body>
<div id="parent">
父節點
<div id="child">
子元素
</div>
</div>
<input type="button" id="insertNode" value="替換節點" />
</body>
<script>
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").onclick = function(){
var newNode = document.createElement("div");
newNode.textContent = "新節點"
parent.replaceChild(newNode,child)
}
複製代碼
移除節點
回憶jquery的節點操做 jQuery DOM 操做(基本操做、內部插入、外部插入、包裹操做) / jQuery DOM節點的建立、插入、刪除
element.setAttribute(name, value)
let div1 = document.getElementById("div1");
div1.setAttribute("align", "center");
複製代碼
element.getAttribute(attributeName)
let div1 = document.getElementById("div1");
let align = div1.getAttribute("align");
複製代碼
element.removeAttribute(attrName)
let div = document.getElementById("div1")
div.removeAttribute("style");
複製代碼
暫瞭解,基本不使用除了前四種
---》獲取CSS衍生出一個問題getComputedStyle()與currentStyle()、style()方法
obj.style:這個方法只能JS只能獲取寫在html標籤中的寫在style屬性中的值(style=」…」),而沒法獲取定義在<style type="text/css" 裏面的屬性
IE中使用的是obj.currentStyle方法,而FF是用的是getComputedStyle 方法
解決兼容問題
function getStyle(element, attr) {
if(element.currentStyle) {
return element.currentStyle[attr];
} else {
return getComputedStyle(element, false)[attr];
}
}
複製代碼
JavaScript 操做 DOM 的那些坑 /// 建議看看在新窗口中打開頁面?當心有坑! /// 前端性能優化之 DOM 篇 /// 前端性能優化之圖片篇 /// 關於反爬蟲(爬蟲與反爬蟲),看這一篇就夠了 /// 瀏覽器特性與安全策略 /// 【乾貨】淺析瀏覽器安全 /// 如何應對網站反爬蟲策略?如何高效地爬大量數據? /// 淺析最煩人的反爬蟲手段 /// 反AdBlock屏蔽廣告
IE8不兼容addEvntListener,只能使用attachEvent()和detachEventListener()方法。IE從IE9開始支持addEventListener和removeEventListener. addEventListener/attachEvent兼容IE瀏覽器與標準瀏覽器
鼠標滾輪事件:mousewheel:》當用戶使用鼠標滾輪與頁面進行交互、在垂直方向上滾動頁面時(不管上下),就會觸發mousewheels事件,這個事件能夠在任何元素上觸發,最終會冒泡到document或者window上。
對應的event對象還有wheelDelta屬性,值是120/-120的倍數
document.addEventListener('mousewheel',function(event){
console.log(event.wheelDelta)
})
複製代碼
鍵盤事件
鍵盤事件用來描述鍵盤行爲,主要有keydown、keypress、keyup三個事件
按下enter鍵
function enterOperate(evt){
if(evt.keyCode == 13){
alert("按下enter鍵");
}
}
document.body.addEventListener("keydown", enterOperate)
複製代碼
組合按鍵
function combinationKey(evt){
if( evt.shiftKey && evt.keyCode == 73){
alert(「觸發了shift + i組合鍵」);
}
}
複製代碼
程序員發展到必定的水平後,瓶頸並不在技術水平上,而是在表達能力上
涉及到點擊穿透的問題【web開發經典問題】(面試題)點擊穿透
用戶點擊事件以後,瀏覽器會等待300ms後,若是沒有(再次的點擊行爲)【雙擊】行爲,再去執行click事件
瀏覽器等待約 300ms 的緣由是,判斷用戶是不是雙擊(double tap)行爲,雙擊過程當中就不適合觸發 click 事件了 爲了減小這300ms的延遲,tap事件被不少框架(如zepto)封裝,來減小這延遲問題, tap事件不是原生的,因此是封裝的
fastclick庫。github
上面說到點擊問題,其實能夠再扯到節流與防抖 面試準備 - JS 防抖與節流
講到節流防抖---拉扯到迴流重繪的問題
最後說到整個瀏覽器從輸入url過程:DNS解析>tcp三次握手>tcp四次揮手>瀏覽器的渲染頁面
解決說明問題【倒序解決】
輸入url以後發生了什麼 參考瀏覽器輸入 URL 後發生了什麼?
DNS解析 :( Domain Name System)是「域名系統」的英文縮寫,它用於TCP/IP網絡,它所提供的服務是用來將主機名和域名轉換爲IP地址的工做。
tcp三次握手:
專業名詞
過程
第一次握手:狀態【兩端都處於CLOSED關閉狀態】,客戶端(Client)將SYN的數據包【SYN=1,隨機產生一個值seq=x】發送給服務端(Server),客戶端(Client)處於SYN-SENT,等到服務端確認
第二次握手:服務端(Server)接收到數據包後,得知客戶端請求創建鏈接,服務端將SYN與ACK【SYN=1,ACK=1,ack = x + 1,隨機產生一個值 seq = y】)的數據包發回給客戶端(Client),服務端進入SYN-RCVD狀態,操做系統爲該 TCP 鏈接分配 TCP 緩存和變量
第三次握手:客戶端(Client)收到確認後,檢查原先本身的seq是否等於x+1,ACK是否爲1,若是沒問題,將ACK數據包【ack = y + 1】丟回服務端(server),Server 檢查 ack 是否爲 y + 1,ACK 是否爲 1,若是正確則鏈接創建成功。狀態【兩端都處於established 狀態】,完成握手,隨後 Client 和 Server 就能夠開始傳輸數據。
TCP四次揮手
看懂上面的而後這裏簡單說:第一次揮手:客戶端發送FIN【FIN=1,seq=u】數據包,中止再發送數據,主動關閉 TCP 鏈接,進入 FIN-WAIT-1(終止等待1)狀態
第二次揮手:服務端接收到FIN數據包報文後,就會發出確認報文段ACK數據包【ACK = 1,確認號 ack = u + 1,序號 seq = v】,Server 進入 CLOSE-WAIT(關閉等待)狀態,此時的 TCP 處於半關閉狀態,Client 到 Server 的鏈接釋放。 ----------------------------------------------------------》數據發送完畢
第三次揮手:Server 已經沒有要向 Client 發出的數據了,Server 發出鏈接釋放報文段(數據包)FIN與ACK(FIN = 1,ACK = 1,序號 seq = w,確認號 ack = u + 1),Server 進入 LAST-ACK(最後確認)狀態,等待 Client 的確認。
第四次揮手:Client 收到 Server 的鏈接釋放報文段後,對此發出確認報文段(ACK = 1,seq = u + 1,ack = w + 1),Client 進入 TIME-WAIT(時間等待)狀態。此時 TCP 未釋放掉,須要通過時間等待計時器設置的時間 2MSL 後,Client 才進入 CLOSED 狀態。
四次揮手的詳述 Client端發起中斷鏈接請求,也就是發送FIN報文。Server端接到FIN報文後,意思是說"我Client端沒有數據要發給你了",可是若是你還有數據沒有發送完成,則沒必要急着關閉Socket,能夠繼續發送數據。因此你先發送ACK,"告訴Client端,你的請求我收到了,可是我還沒準備好,請繼續你等個人消息"。這個時候Client端就進入FIN_WAIT狀態,繼續等待Server端的FIN報文。當Server端肯定數據已發送完成,則向Client端發送FIN報文,"告訴Client端,好了,我這邊數據發完了,準備好關閉鏈接了"。Client端收到FIN報文後,"就知道能夠關閉鏈接了,可是他仍是不相信網絡,怕Server端不知道要關閉,因此發送ACK後進入TIME_WAIT狀態,若是Server端沒有收到ACK則能夠重傳。「,Server端收到ACK後,"就知道能夠斷開鏈接了"。Client端等待了2MSL後依然沒有收到回覆,則證實Server端已正常關閉,那好,我Client端也能夠關閉鏈接了。Ok,TCP鏈接就這樣關閉了
瀏覽器的渲染:【乾貨】十分鐘讀懂瀏覽器渲染流程 /// 瀏覽器渲染頁面過程與頁面優化
迴流與重繪:重點:瀏覽器的迴流與重繪 (Reflow & Repaint) /// 你真的瞭解迴流和重繪嗎
回到最初的問題:節流與防抖(這是倆種解決方案:解決的問題是:頻繁的點擊事件)
節流:指定一段時間內只執行一次操做
節流函數體
function throttle(fn) {
// 四、經過閉包保存一個標記canRun
let canRun = true;
return function() {
// 五、在函數開頭判斷標誌是否爲 true,不爲 true 則中斷函數
//做用執行函數canRun爲false,而後一段時間後才變成true
if(!canRun) {
return;
}
// 六、將 canRun 設置爲 false,防止執行以前再被執行
canRun = false;
// 七、定時器
setTimeout( () => {
fn.call(this, arguments); //將不肯定變量替換到函數中了
// 八、執行完事件(好比調用完接口)以後,從新將這個標誌設置爲 true
canRun = true;
}, 1000);
};
// 三、須要節流的事件
function sayThrottle() {
console.log("節流成功!");
}
複製代碼
懶加載要監聽計算滾動條的位置,使用節流按必定時間的頻率獲取。 用戶點擊提交按鈕,假設咱們知道接口大體的返回時間的狀況下,咱們使用節流,只容許必定時間內點擊一次。
防抖函數:倆次觸發間隔超過指定時間間隔
window.onload = function() {
// 一、獲取這個按鈕,並綁定事件
var myDebounce = document.getElementById("debounce");
myDebounce.addEventListener("click", debounce(sayDebounce));
}
// 二、防抖功能函數,接受傳參
function debounce(fn) {
// 四、建立一個標記用來存放定時器的返回值
let timeout = null;
return function() {
// 五、每次當用戶點擊/輸入的時候,把前一個定時器清除
clearTimeout(timeout);
// 六、而後建立一個新的 setTimeout,
// 這樣就能保證點擊按鈕後的 interval 間隔內
// 若是用戶還點擊了的話,就不會執行 fn 函數
timeout = setTimeout(() => {
fn.call(this, arguments);
}, 1000);
};
}
// 三、須要進行防抖的事件處理
function sayDebounce() {
// ... 有些須要防抖的工做,在這裏執行
console.log("防抖成功!");
}
複製代碼
輸入URL發生了什麼=參考文章
DNS解析:DNS解析的過程是什麼,求詳細的? /// DNS解析過程詳解
TCP三次握手以及TCP四次揮手 TCP三次握手與四次揮手 /// 注意!是TCP不是HTTP的3次握手與4次揮手(#...#)
瀏覽器的渲染:【乾貨】十分鐘讀懂瀏覽器渲染流程