TML5是以HTML4為基礎(chǔ)的,并對(duì)HTML4進(jìn)行了大量的修改。
首先看看語法上的改變、
對(duì)于HTML4而言,HTML5在語法上發(fā)生了很大的變化。對(duì)于這些變化,有些人開始有點(diǎn)不安,是不是又要開始重新學(xué)習(xí)HTML語言了?但是,HTML5中的語法變化,與其他開發(fā)語言中的語法變化有根本的不同。因?yàn)樵贖TML5之前幾乎沒有符合標(biāo)準(zhǔn)規(guī)范的web瀏覽器導(dǎo)致的。
HTML的語法是在SGML語言的基礎(chǔ)上建立起來的。但是SGML語法非常之復(fù)雜,要開發(fā)能夠解析SGML語法的程序也很不容易,因此很多瀏覽器都不包含SGML的分析器。因此,雖然HTML基本上遵從SGML的語法,但是對(duì)于HTML的執(zhí)行在各瀏覽器之間并沒有一個(gè)統(tǒng)一的標(biāo)準(zhǔn)。
為解決各瀏覽器之間的互兼容性和互操作性,就得要有一個(gè)統(tǒng)一的標(biāo)準(zhǔn),所以HTML5就是圍繞這個(gè)web標(biāo)準(zhǔn),重新定義了一套在現(xiàn)有HTML的基礎(chǔ)上修改而來的語法,以便各瀏覽器在運(yùn)行HTML的時(shí)候能夠符合一個(gè)通用標(biāo)準(zhǔn)。
作為一名Web開發(fā)者,可能你并沒有對(duì)這個(gè)“H5”這個(gè)字眼投入太多的關(guān)注,但實(shí)際上它早已不知不覺進(jìn)入到你的開發(fā)中,并且總有一天會(huì)讓你不得不正視它,了解它并運(yùn)用它
打個(gè)比方:《海賊王》中的主角路飛在“頂上戰(zhàn)爭兩年前”,會(huì)在一些危急關(guān)頭“不經(jīng)意”地使用霸王色霸氣,但對(duì)”霸氣“的結(jié)構(gòu)體系和具體運(yùn)用都不太了解,這讓他在香波地群島等諸多重大戰(zhàn)役中大吃苦頭。此后, 他不惜花費(fèi)兩年時(shí)間跟隨雷利修煉霸氣。因?yàn)椋?strong>如果不去了解這個(gè)嶄新的戰(zhàn)斗方法的話,他們?cè)跉埧岬男率澜缫惶煲采娌涣恕?/strong>
咳咳, 回到主題,為什么我們要學(xué)HTML5呢?
1. 了解HTML5的囊括范圍的一大好處是:當(dāng)你不小心使用了一個(gè)H5的東東的時(shí)候(例如你試圖通過百度找到的答案解決一個(gè)緊張的需求),你會(huì)很及時(shí)的關(guān)注它的兼容性
2. H5有些新增的特性也許你從沒接觸過,也感覺無需用到它。但就在不久的將來,你可能就會(huì)用到,甚至依賴于它(畢竟這就是HTML的未來)
在下面, 我將學(xué)習(xí)H5中的知識(shí)點(diǎn)分成兩類:主要知識(shí)點(diǎn)和針對(duì)特定功能的知識(shí)點(diǎn),其中對(duì)主要知識(shí)點(diǎn)的部分,從學(xué)習(xí)成本的角度對(duì)其進(jìn)行了難度分級(jí)
(僅屬個(gè)人觀點(diǎn)!如有改進(jìn)意見,歡迎討論)
一.主要知識(shí)點(diǎn)
(從需求層面上來說,普及范圍相對(duì)較廣)
相對(duì)容易的部分:
1.在線和離線事件(Online/Offline) (相對(duì)容易)
2. 眾多的新增元素 如<output>, <progress>等 (相對(duì)容易)
3. history關(guān)于歷史狀態(tài)管理的API (相對(duì)容易)
4 Storage(localStorage和sessionStorage) (相對(duì)容易)
相對(duì)較難的部分:
5. Web Worker (相對(duì)較難)
6. canvas (相對(duì)較難)
7. indexedDB (相對(duì)較難)
8. 拖放操作 (相對(duì)較難)
9. Web Sockets (相對(duì)較難)
二. 針對(duì)特定功能的知識(shí)點(diǎn)
(對(duì)需求來說,主要針對(duì)某一方面的特殊需求場景)
1. 對(duì)音視頻的支持
2. Camera API (操作攝像頭)
3. WebGL (3D圖像)
4. 地理位置定位 (geolocation對(duì)象)
本文主要講述H5中主要知識(shí)點(diǎn)中,學(xué)習(xí)成本相對(duì)較高的四個(gè)點(diǎn)(僅個(gè)人觀點(diǎn)):
一.Web Worker
二.canvas
三.indexedDB
四.拖放操作
【注意】因?yàn)橄旅娼榻B的H5的特性在一些比較老的瀏覽器里可能遇到兼容性問題,所以你在使用前必須要能力檢測(cè),例如這樣
Web Worker的機(jī)制讓你能夠創(chuàng)建一個(gè)在后臺(tái)線程運(yùn)行的腳本,這個(gè)腳本不會(huì)對(duì)我們當(dāng)前執(zhí)行任務(wù)的腳本造成任何干擾(例如阻塞),同時(shí)Web Worker提供了一套API使你能夠在當(dāng)前腳本和后臺(tái)腳本間進(jìn)行數(shù)據(jù)的互相傳輸(worker)
“一套API, 兩個(gè)對(duì)象”
我們現(xiàn)在已知的關(guān)于Web Worker的機(jī)制是: 有一個(gè)當(dāng)前腳本, 和一個(gè)在后臺(tái)運(yùn)行的worker腳本,所以我們問題的關(guān)鍵就落在了這兩個(gè)腳本的通信(數(shù)據(jù)交互)上
通過
生成了“兩個(gè)對(duì)象”(你可能會(huì)問:為什么是兩個(gè)不是一個(gè)呢?請(qǐng)往下看)
“第一個(gè)”對(duì)象是我們?cè)诋?dāng)前腳本中通過構(gòu)造函數(shù)顯式創(chuàng)建出來的worker對(duì)象,它擁有一套API:postMessage和onmessage,通過postMessage方法可以向worker腳本(上文worker.js)發(fā)送數(shù)據(jù), 通過onmessage方法可以從worker腳本接收數(shù)據(jù)
“第二個(gè)”對(duì)象是在Web Worker腳本(上文的worker.js)中隱式創(chuàng)建出來的全局變量對(duì)象,它叫DedicatedWorkerGlobalScope(這個(gè)時(shí)候在work.js全局變量對(duì)象是它而不是Window!!),而它也擁有一套API:postMessage和onmessage,通過postMessage方法可以向當(dāng)前執(zhí)行任務(wù)的腳本發(fā)送數(shù)據(jù), 通過onmessage方法可以從當(dāng)前執(zhí)行任務(wù)的腳本接收數(shù)據(jù)
【注意】關(guān)于DedicatedWorkerGlobalScope
1. 它是在Web Worker腳本中生成的特殊的全局變量對(duì)象,也就是在全局執(zhí)行環(huán)境中使用this指向的不是Window而是它
2. 它不能像Windows那樣通過變量名直接訪問,但在Web Worker腳本中你能通過this取到它
所以現(xiàn)在數(shù)據(jù)傳遞方向有兩條:
1. 調(diào)用當(dāng)前腳本中worker對(duì)象的postMessage方法, 然后在Web Worker腳本(上文的worker.js)中通過onmessage這個(gè)回調(diào)方法接收數(shù)據(jù)
2. 調(diào)用Web Worker腳本中的this.postMessage方法(this指向DedicatedWorkerGlobalScope),然后在當(dāng)前腳本中worker對(duì)象的onmessage回調(diào)方法接收數(shù)據(jù)
看到這里可能有點(diǎn)懵,來讓我們通過一個(gè)例子看看1中的數(shù)據(jù)傳遞:
先看示例吧,這是我們的目錄結(jié)構(gòu)
index.html:
main.js:
worker.js:
點(diǎn)擊按鈕后,在main.js中調(diào)用worker對(duì)象的postMessage方法, 這個(gè)數(shù)據(jù)就被發(fā)給了work.js中的全局變量對(duì)象DedicatedWorkerGlobalScope, 所以我們?cè)趙ork,js中通過this.onmessage接收數(shù)據(jù)并輸出
【注意】postMessage傳遞的參數(shù)會(huì)被“原封不動(dòng)”地傳遞給onmessage中event對(duì)象的data屬性
例如:
postMessage([1,2,3]) ——> this.onmessage = function (e) { } 中 e.data === [1,2,3]
postMessage({a:1,b: 2}) ——> this.onmessage = function (e) { } 中 e.data === {a:1,b: 2}
我們上面的例子展現(xiàn)的是從當(dāng)前任務(wù)腳本向worker腳本傳遞數(shù)據(jù),那么同樣的道理,我們也能從work腳本向當(dāng)前任務(wù)腳本傳遞數(shù)據(jù)(方式相同)
例子:
index.html:
同上
main.js:
worker.js:
demo如下
點(diǎn)擊傳遞數(shù)據(jù)輸出:
cancas是H5新增的一個(gè)標(biāo)簽,把canvas翻譯過來就是畫布,顧名思義,這是用來”畫畫的“,那畫畫的”畫筆“是什么呢? 它就是和canvas元素對(duì)象對(duì)應(yīng)的一個(gè)”上下文對(duì)象“(context),這里的這個(gè)上下文對(duì)象可能和你印象中的”上下文“有較大的差異,它只是個(gè)單純的包含了一系列“繪畫”方法的對(duì)象,下面我們介紹的關(guān)于canvas的內(nèi)容都要圍繞這個(gè)"canvas上下文對(duì)象"展開
我們可以通過這種方式取得canvas上下文對(duì)象:
假設(shè)這是我們的HTML:
這樣取得上下文對(duì)象:
下面展現(xiàn)的是上下文對(duì)象的一些繪制圖形的方法(它們都可以被ctx調(diào)用)
上面的x,y代表相對(duì)于canvas畫布左上角的橫縱坐標(biāo):
例子:
html部分:
JS部分:
【注意】. canvas標(biāo)簽內(nèi)的內(nèi)容(例如上面的文本)是否呈現(xiàn)取決于瀏覽器是否支持canvas,如果支持,則不出現(xiàn),如果不支持,則會(huì)呈現(xiàn)出來
demo:
我們以上面的為基礎(chǔ)稍作修改:
demo:
demo:
這里要稍微提一下, 也許上面的那些繪制圖形,繪制文本的操作對(duì)你來說都沒有觸動(dòng),因?yàn)樗鼈冸x我們的直接需求似乎還有一定的距離,但我想接下來的這幾個(gè)上下文API你或許有些興趣。
例如我們可能有一個(gè)需求是載入已有的圖片,對(duì)它截圖(裁剪)后保存為一張新的圖片,這個(gè)時(shí)候我們就可以使用到canvas的繪制圖片,裁剪圖片,保存圖片的API了
通過canvas上下文對(duì)象的drawImage方法可直接繪制圖片
我們可以通過下面的一段代碼動(dòng)態(tài)獲取img元素對(duì)象
廢話不多說,直接上demo!
在相同目錄下有這么一張圖片
JS代碼:
demo:
我們發(fā)現(xiàn), 圖片加載完成后被寫入了畫布當(dāng)中!
canvas上下文對(duì)象的clip方法可根據(jù)路徑對(duì)canvas畫布進(jìn)行裁剪
讓我們?cè)谠瓉淼幕A(chǔ)上添加一點(diǎn)東西:
【注意】clip方法的調(diào)用要在drawImage方法之前,否則不能成功! 也就是說要“先裁剪,再畫圖”
canvas的保存和導(dǎo)出
我們通過document.getElementById("canvas")取得的畫布對(duì)象,有一個(gè)toDataURL()方法,可將當(dāng)前畫布作為一張圖片,并返回其base64編碼格式的數(shù)據(jù),這在保存圖片的時(shí)候非常有用
toDataURL接受兩個(gè)參數(shù):圖片類型和質(zhì)量參數(shù)
canvas.toDataURL(圖片類型,質(zhì)量參數(shù))
看下面的例子
控制臺(tái)輸出了base64格式的數(shù)據(jù):
我們通過網(wǎng)上的還原軟件看看會(huì)把這個(gè)base64數(shù)據(jù)還原成什么圖片:
正是我們想要的圖片
indexedDB是存在于瀏覽器中的數(shù)據(jù)庫,它和一般的數(shù)據(jù)庫一樣有寫改刪查的功能,不同之處在于:常見的數(shù)據(jù)庫一般是在服務(wù)器上,并且要求我們的應(yīng)用在線時(shí)才可以工作,而indexedDB使得在離線的時(shí)候讀取數(shù)據(jù)成為了可能。下面,我就給大家介紹一下這個(gè)“駐扎”在瀏覽器上的特殊的數(shù)據(jù)庫吧
我們首先要做的事情,當(dāng)然是創(chuàng)建(或打開)一個(gè)數(shù)據(jù)庫,這要用到indexedDB對(duì)象的open方法
它接收兩個(gè)參數(shù): 數(shù)據(jù)庫名稱和數(shù)據(jù)庫版本(第二個(gè)參數(shù)是可選的)
調(diào)用open方法時(shí)候,如果對(duì)應(yīng)名稱的數(shù)據(jù)庫不存在,則創(chuàng)建一個(gè)新的數(shù)據(jù)庫,如果已存在,則打開已存在的那個(gè)數(shù)據(jù)庫
需要說明的是, indexedDB里面絕大多數(shù)操作都是異步的, 上述的indexedDB.open并不會(huì)立即創(chuàng)建一個(gè)數(shù)據(jù)庫, 你需要在異步的回調(diào)里面判斷數(shù)據(jù)庫是否創(chuàng)建成功,并對(duì)可能出現(xiàn)的錯(cuò)誤做判斷和處理
只有在onsuccess回調(diào)中,你才能通過request.result取得創(chuàng)建成功的數(shù)據(jù)庫
通過open返回的request對(duì)象有三個(gè)回調(diào):
onsuccess 每次創(chuàng)建/打開數(shù)據(jù)庫時(shí)候都會(huì)調(diào)用
onerror 創(chuàng)建/打開數(shù)據(jù)庫發(fā)生錯(cuò)誤的時(shí)候調(diào)用
onupgradeneeded 數(shù)據(jù)庫版本變化的時(shí)候調(diào)用 (onupgradeneeded 是我們唯一可以修改數(shù)據(jù)庫結(jié)構(gòu)的地方)
open一個(gè)indexedDB數(shù)據(jù)庫后,一般在onupgradeneeded回調(diào)中初始化(或修改)數(shù)據(jù)庫結(jié)構(gòu)(劃重點(diǎn)?。。?/strong>
這包括兩個(gè)方面的操作:
1. 通過db.createObjectStore創(chuàng)建對(duì)象存儲(chǔ)空間,并取得ObjectStore對(duì)象(類似于SQL數(shù)據(jù)庫中的建表操作)
2. 通過調(diào)用ObjectStore.createIndex創(chuàng)建該存儲(chǔ)空間內(nèi)的索引( 以便于提高查詢時(shí)候的速度)
具體的可看下面的例子:
運(yùn)行一下, 然后讓我們看看效果:
打開chrome的Application面板,點(diǎn)擊左欄的Storage下的indexedDB使其展開
就可以看到我們新創(chuàng)建的phwDataBase數(shù)據(jù)庫, 以及它內(nèi)部的people數(shù)據(jù)存儲(chǔ)空間了
(右邊展示的是people數(shù)據(jù)存儲(chǔ)空間的具體內(nèi)容,因?yàn)楝F(xiàn)在什么數(shù)據(jù)都還沒添加,所以key和value兩列下是沒有內(nèi)容的)
看了上面的代碼你可能會(huì)有些疑惑
onupgradeneeded 和onsuccess回調(diào)的關(guān)系是怎樣的? 為什么我們必須在.onupgradeneeded中初始化數(shù)據(jù)庫的結(jié)構(gòu),而不是在onsuccess中?
這主要是由兩個(gè)回調(diào)調(diào)用的時(shí)機(jī)決定的:
1.對(duì) onsuccess回調(diào),在每次數(shù)據(jù)庫創(chuàng)建/打開的時(shí)候都會(huì)調(diào)用(不僅是第一次創(chuàng)建的時(shí)候會(huì)調(diào)用,每次打開的時(shí)候也都會(huì)調(diào)用)
2. 對(duì)onupgradeneeded回調(diào),在open提供第二個(gè)版本參數(shù)的前提下:
2.1 第一次調(diào)用open方法創(chuàng)建一個(gè)新的數(shù)據(jù)庫的時(shí)候,onupgradeneeded一定會(huì)被調(diào)用
2.2 第二次或以后open該數(shù)據(jù)庫,只在版本參數(shù)改變的時(shí)候, onupgradeneeded才會(huì)被調(diào)用
【注意】在缺少第二個(gè)版本參數(shù)的情況下,onupgradeneeded永遠(yuǎn)不會(huì)被調(diào)用??!
所以說:
1.open數(shù)據(jù)庫的時(shí)候,最(yi)好(ding)要帶上第二個(gè)參數(shù)(版本參數(shù))
2. 修改數(shù)據(jù)庫結(jié)構(gòu)(例如創(chuàng)建和刪除對(duì)象存儲(chǔ)空間以及構(gòu)建和刪除索引。)要在onupgradeneeded回調(diào)中運(yùn)行
(很顯然每次打開都會(huì)被調(diào)用的onsuccess并不適合用于初始化數(shù)據(jù)庫結(jié)構(gòu))
首先說一下,在下面的展示例子中,我們的HTML是這樣的
demo:
這里要說明一下的是,indexedDB的操作是以事務(wù)為基礎(chǔ)的。 所以,對(duì)存儲(chǔ)空間(objectStore)的操作都要基于事務(wù)來進(jìn)行。 具體點(diǎn)說,就是需要先通過db.transaction()方法取得transaction對(duì)象,然后再通過transaction.objectStore()方法取得目標(biāo)objectStore,再然后才能調(diào)用objectStore的API進(jìn)行“寫改刪查”
打個(gè)比方, 如果說我們存儲(chǔ)的數(shù)據(jù)是糧食的話, objectStore就是一個(gè)個(gè)并排的糧倉,你可以往里面運(yùn)糧食,也可以把糧食運(yùn)出去, 但你對(duì)“糧食”做任何行為前, 都要和糧倉門前的守衛(wèi)—— transaction(事務(wù))“打聲招呼”,得到準(zhǔn)許才能進(jìn)入糧倉
有兩個(gè)方法要說一下
1. transaction方法
transaction 方法 一般接受兩個(gè)參數(shù),并返回一個(gè)事務(wù)對(duì)象。
1.1第一個(gè)參數(shù)是一個(gè)數(shù)組, 一個(gè)我們希望事務(wù)能夠操作的objectStore所組成的數(shù)組,如果你希望這個(gè)事務(wù)能夠操作所有的objectStore,那么傳入空數(shù)組[]即可
1.2 第二個(gè)參數(shù)是一個(gè)字符串, 默認(rèn)是“onlyread”, 如果我們有需要對(duì)數(shù)據(jù)進(jìn)行寫操作的需求的話可傳入“readwrite”
例如我們下面的一行代碼:
2. transaction.objectStore方法
這個(gè)方法接受一個(gè)參數(shù): 指定的objectStore的名稱, 方法返回的是獲取到的objectStore
例如我們下面的一行代碼:
寫操作的關(guān)鍵在于objectStore.add(XXX);方法,其中XXX是我們初始化objectStore時(shí)候?qū)懭氲摹爸麈I”
也就是 var objectStore = db.createObjectStore("people", { keyPath: "id" }); (這段代碼在上面)中keyPath的值——id
demo:
點(diǎn)擊“增加數(shù)據(jù)”后彈出
再看看application面板下的indexedDB:
我們已經(jīng)成功添加了三條數(shù)據(jù)進(jìn)去了
刪操作的關(guān)鍵在于objectStore.delete(XXX);方法,其中XXX是我們初始化objectStore時(shí)候?qū)懭氲摹爸麈I”
也就是 var objectStore = db.createObjectStore("people", { keyPath: "id" }); 中keyPath的值——id
點(diǎn)擊上面的“刪除數(shù)據(jù)”按鈕(刪除id = 1的數(shù)據(jù))
再來看看, id為1的那一行已經(jīng)被刪除了
刪操作的關(guān)鍵在于objectStore.get(XXX);方法
demo:
點(diǎn)擊“獲取數(shù)據(jù)”按鈕,彈出
(這里固定查找id為2的數(shù)據(jù))
遍歷數(shù)據(jù)需要用到游標(biāo)
通過 objectStore.openCursor()可創(chuàng)建一個(gè)游標(biāo)對(duì)象(cursor), 這個(gè)cursor對(duì)象包含兩個(gè)屬性值: key和value
key就是我們一直說的那個(gè)“主鍵”, 而value是我們存入的時(shí)候的那個(gè)對(duì)象,通過 cursor.continue方法可以使得游標(biāo)向下移動(dòng)
點(diǎn)擊“遍歷全部數(shù)據(jù)”按鈕,看看控制臺(tái)
我們通過objectStore.get方法,通過查找主鍵的方式查找對(duì)應(yīng)的對(duì)象數(shù)據(jù)的方式是很快的。
但如果我們通過非主鍵的數(shù)據(jù)去查找對(duì)應(yīng)的那個(gè)對(duì)象就非常慢了,這個(gè)時(shí)候我們需要?jiǎng)?chuàng)建一個(gè)索引并通過索引來查找, 從而獲得較快的速度:
點(diǎn)擊“通過索引獲取數(shù)據(jù)”按鈕:
好! 現(xiàn)在讓我們對(duì)indexedDB做一個(gè)小小的總結(jié):
1. indexedDB是面向?qū)ο蟮模?與傳統(tǒng)的以二維表為基礎(chǔ)的數(shù)據(jù)庫不同
2. IndexedDB是一個(gè)事務(wù)型數(shù)據(jù)庫系統(tǒng)
3. indexedDB大多數(shù)API都是異步的,這意味著調(diào)用一個(gè)方法你不能馬上得到關(guān)鍵的那個(gè)對(duì)象,而在對(duì)應(yīng)的success回調(diào)中才能取得
一個(gè)典型的拖放操作是這樣開始的:用戶用鼠標(biāo)選中一個(gè)可拖動(dòng)的(draggable)元素,移動(dòng)鼠標(biāo)到一個(gè)可放置的(droppable)元素,然后釋放鼠標(biāo)。 在操作期間,會(huì)觸發(fā)一系列的拖放類型的事件
其中我們主要關(guān)心的事件有三個(gè):
1. ondragstart 發(fā)生在可拖拽(draggable)的元素上, 在元素被拖動(dòng)的時(shí)候調(diào)用
2. ondragover 發(fā)生在可放置(droppable)的元素上, 當(dāng)某被拖動(dòng)的對(duì)象在可放置對(duì)象范圍內(nèi)(上方)時(shí)觸發(fā)此事件
3. ondrop 發(fā)生在可放置(droppable)的元素上,當(dāng)釋放鼠標(biāo)使可拖拽元素“放進(jìn)”可放置元素內(nèi)的瞬間觸發(fā)。
需思考的問題:
1. 如何使得被拖拽元素可拖拽?(因?yàn)樵啬J(rèn)的行為是不可拖拽的),以及如何使得被放置的容器元素可放置? (因?yàn)樵啬J(rèn)是不可放置的)
對(duì)前者, 我們可以為元素設(shè)置draggable屬性,并且設(shè)置為true
對(duì)后者, 我們可以在被放置的容器元素中的ondragover事件里通過event.preventDefault();阻止默認(rèn)行為——禁止放置
2.如何實(shí)現(xiàn)“脫 — 放”過程的數(shù)據(jù)傳遞?
這里首先需要知道的是,當(dāng)我們拖動(dòng)一個(gè)圖片到另一個(gè)地方的時(shí)候,我們是不能“直接把圖片拖拽進(jìn)去”的,也就是說,我們還是要通過以下的思路實(shí)現(xiàn)拖放:
在被放置的元素中取得被拖拽元素的相關(guān)數(shù)據(jù)(如id),然后通過appendChild之類的API實(shí)現(xiàn)添加被拖拽的元素,從而模擬整個(gè)拖拽的過程
也就是說, 拖拽其實(shí)可分為三個(gè)過程: 拖動(dòng)—傳遞被拖動(dòng)元素的數(shù)據(jù)(如id)—在容器元素中添加該元素
關(guān)鍵在于如何在被拖動(dòng)元素和被放置元素中傳遞數(shù)據(jù),這可以通過event.dataTransfer對(duì)象來實(shí)現(xiàn)
dataTransfer可以通過setData方法添加拖動(dòng)數(shù)據(jù),并通過getDate方法取得拖動(dòng)數(shù)據(jù),我們可以在
ondragstart事件和ondrop事件中調(diào)用這兩個(gè)方法, 實(shí)現(xiàn)關(guān)鍵性的數(shù)據(jù)傳遞。
具體請(qǐng)看下面的例子:
拖拽前
拖拽后
參考資料:
HTML5-MDN https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/HTML5
【完】
CSS 選擇器有很多,不同的選擇器的權(quán)重和優(yōu)先級(jí)不一樣,對(duì)于一個(gè)元素,如果存在多個(gè)選擇器,那么就需要根據(jù)權(quán)重來計(jì)算其優(yōu)先級(jí)。
權(quán)重分為四級(jí),分別是:
需要注意的是:通用選擇器(\*)、子選擇器(>)和相鄰?fù)x擇器(+)并不在這四個(gè)等級(jí)中,所以他們的權(quán)值都為 0。 權(quán)重值大的選擇器其優(yōu)先級(jí)也高,相同權(quán)重的優(yōu)先級(jí)又遵循后定義覆蓋前面定義的情況。
1. 什么是“盒子”
初學(xué) CSS 的朋友,一開始學(xué) CSS 基礎(chǔ)知識(shí)的時(shí)候一定學(xué)過padding border和margin,即內(nèi)邊距、邊框和外邊距。它們?nèi)呔蜆?gòu)成了一個(gè)“盒子”。就像我們收到的快遞,本來買了一部小小的手機(jī),收到的卻是那么大一個(gè)盒子。因?yàn)槭謾C(jī)白色的包裝盒和手機(jī)機(jī)器之間有間隔層(內(nèi)邊距),手機(jī)白色盒子有厚度,雖然很?。ㄟ吙颍凶雍涂爝f箱子之間還有一層泡沫板(外邊距)。這就是一個(gè)典型的盒子。
如上圖,真正的內(nèi)容就是這些文字,文字外圍有 10px 的內(nèi)邊距,5px 的邊框,10px 的外邊距??吹胶凶恿税??
題目:盒子模型的寬度如何計(jì)算
2. 固定寬度的盒子
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px;">
文章言簡意賅的介紹的瀏覽器的工作過程,web前端
</div>
如上圖,得到網(wǎng)頁效果之后,我們可以用截圖工具來量一下文字內(nèi)容的寬度。發(fā)現(xiàn),文字內(nèi)容的寬度剛好是 300px,也就是我們?cè)O(shè)置的寬度。
因此,在盒子模型中,我們?cè)O(shè)置的寬度都是內(nèi)容寬度,不是整個(gè)盒子的寬度。而整個(gè)盒子的寬度是:(內(nèi)容寬度 + border寬度 + padding寬度 + margin寬度)之和。這樣我們改四個(gè)中的其中一個(gè),都會(huì)導(dǎo)致盒子寬度的改變。這對(duì)我們來說不友好。
沒關(guān)系,這個(gè)東西不友好早就有人發(fā)現(xiàn)了,而且已經(jīng)解決,下文再說。
3. 充滿父容器的盒子
默認(rèn)情況下,div是display:block,寬度會(huì)充滿整個(gè)父容器。如下圖:
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px;">
之前看過一篇文章,叫做《瀏覽器工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘》,
文章言簡意賅的介紹的瀏覽器的工作過程,web前端
之前看過一篇文章,叫做《瀏覽器工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘》,
文章言簡意賅的介紹的瀏覽器的工作過程,web前端
</div>
4. 包裹內(nèi)容的盒子
這種情況下比較簡單,內(nèi)容的寬度按照內(nèi)容計(jì)算,盒子的寬度將在內(nèi)容寬度的基礎(chǔ)上再增加(padding寬度 + border寬度 + margin寬度)之和。
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px;">
之前看過一篇文章,叫做《瀏覽器工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘》
</div>
5. box-sizing:border-box
前面提到,為盒子模型設(shè)置寬度,結(jié)果只是設(shè)置了內(nèi)容的寬度,這個(gè)不合理。如何解決這一問題?答案就是為盒子指定樣式:box-sizing:border-box。
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px; box-sizing:border-box;">
之前看過一篇文章,叫做《瀏覽器工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘》
</div>
上圖中,為div設(shè)置了box-sizing:border-box之后,300px 的寬度是內(nèi)容 + padding + 邊框的寬度(不包括margin),這樣就比較符合我們的實(shí)際要求了。建議大家在為系統(tǒng)寫 CSS 時(shí)候,第一個(gè)樣式是:
* {
box-sizing:border-box;
}
大名鼎鼎的 Bootstrap 也把box-sizing:border-box加入到它的*選擇器中,我們?yōu)槭裁床贿@樣做呢?
6. 縱向 margin 重疊
這里提到 margin,就不得不提一下 margin 的這一特性——縱向重疊。如<p>的縱向 margin 是 16px,那么兩個(gè)<p>之間縱向的距離是多少?—— 按常理來說應(yīng)該是 16 + 16 = 32px,但是答案仍然是 16px。因?yàn)榭v向的 margin 是會(huì)重疊的,如果兩者不一樣大的話,大的會(huì)把小的“吃掉”。
float 用于網(wǎng)頁布局比較多,使用起來也比較簡單,這里總結(jié)了一些比較重要、需要注意的知識(shí)點(diǎn),供大家參考。
1. 誤解和誤用
float 被設(shè)計(jì)出來的初衷是用于文字環(huán)繞效果,即一個(gè)圖片一段文字,圖片float:left之后,文字會(huì)環(huán)繞圖片。
<div>
<img src="image/1.png" style="float:left">
一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字
</div>
但是,后來大家發(fā)現(xiàn)結(jié)合float + div可以實(shí)現(xiàn)之前通過table實(shí)現(xiàn)的網(wǎng)頁布局,因此就被“誤用”于網(wǎng)頁布局了。
題目:為何 float 會(huì)導(dǎo)致父元素塌陷?
2. 破壞性
float 的破壞性 —— float 破壞了父標(biāo)簽的原本結(jié)構(gòu),使得父標(biāo)簽出現(xiàn)了坍塌現(xiàn)象。導(dǎo)致這一現(xiàn)象的最根本原因在于:被設(shè)置了 float 的元素會(huì)脫離文檔流。其根本原因在于 float 的設(shè)計(jì)初衷是解決文字環(huán)繞圖片的問題。大家要記住 float 的這個(gè)影響。
3. 包裹性
包裹性也是 float 的一個(gè)非常重要的特性,大家用 float 時(shí)一定要熟知這一特性。咱們還是先從一個(gè)小例子看起:
如上圖,普通的 div 如果沒有設(shè)置寬度,它會(huì)撐滿整個(gè)屏幕,在之前的盒子模型那一節(jié)也講到過。而如果給 div 增加float:left之后,它突然變得緊湊了,寬度發(fā)生了變化,把內(nèi)容中的三個(gè)字包裹了——這就是包裹性。為 div 設(shè)置了 float 之后,其寬度會(huì)自動(dòng)調(diào)整為包裹住內(nèi)容寬度,而不是撐滿整個(gè)父容器。
4. 清空格
float 還有一個(gè)大家可能不是很熟悉的特性——清空格。按照慣例,咱還是先舉例子說明。
<div style="border: 2px solid blue; padding:3px;">
<img src="image/1.png"/>
<img src="image/2.png"/>
<img src="image/3.png"/>
<img src="image/4.png"/>
</div>
加上float:left之后:
題目:手寫 clearfix
5. clearfix
清除浮動(dòng)的影響,一般使用的樣式如下,統(tǒng)稱clearfix代碼。所有 float 元素的父容器,一般情況下都應(yīng)該加clearfix這個(gè) class。
.clearfix:after {
content: '';
display: table;
clear: both;
}
.clearfix {
*zoom: 1; /* 兼容 IE 低版本 */
}
<div class="clearfix">
<img src="image/1.png" style="float: left"/>
<img src="image/2.png" style="float: left"/>
</div>
6. 小結(jié)
float 的設(shè)計(jì)初衷是解決文字環(huán)繞圖片的問題,后來誤打誤撞用于做布局,因此有許多不合適或者需要注意的地方,上文基本都講到了需要的知識(shí)點(diǎn)。如果是剛開始接觸 float 的同學(xué),學(xué)完上面的基礎(chǔ)知識(shí)之后,還應(yīng)該做一些練習(xí)實(shí)戰(zhàn)一下 —— 經(jīng)典的“圣杯布局”和“雙飛翼布局”。這里就不再展開講了,網(wǎng)上資料非常多,例如淺談面試中??嫉膬煞N經(jīng)典布局——圣杯與雙飛翼(此文的最后兩張圖清晰地展示了這兩種布局)。
position 用于網(wǎng)頁元素的定位,可設(shè)置 static/relative/absolute/fixed 這些值,其中 static 是默認(rèn)值,不用介紹。
題目:relative 和 absolute 有何區(qū)別?
1. relative
相對(duì)定位 relative 可以用一個(gè)例子很輕松地演示出來。例如我們寫 4 個(gè)<p>,出來的樣子大家不用看也能知道。
<p>第一段文字</p>
<p>第二段文字</p>
<p>第三段文字</p>
<p>第四段文字</p>
然后我們?cè)诘谌齻€(gè)<p>上面,加上position:relative并且設(shè)置left和top值,看這個(gè)<p>有什么變化。
<p>第一段文字</p>
<p>第二段文字</p>
<p style="position:relative; top: 10px; left: 10px">第三段文字</p>
<p>第四段文字</p>
上圖中,大家應(yīng)該要識(shí)別出兩個(gè)信息(相信大部分人會(huì)忽略第二個(gè)信息)
可見,relative 會(huì)導(dǎo)致自身位置的相對(duì)變化,而不會(huì)影響其他元素的位置、大小。這是 relative 的要點(diǎn)之一。還有第二個(gè)要點(diǎn),就是 relative 產(chǎn)生一個(gè)新的定位上下文。下文有關(guān)于定位上下文的詳細(xì)介紹,這里可以先通過一個(gè)例子來展示一下區(qū)別:
注意看這兩圖的區(qū)別,下文將有解釋。
2. absolute
還是先寫一個(gè)基本的 demo。
<p>第一段文字</p>
<p>第二段文字</p>
<p style="background: yellow">第三段文字</p>
<p>第四段文字</p>
然后,我們把第三個(gè)<p>改為position:absolute;,看看會(huì)發(fā)生什么變化。
從上面的結(jié)果中,我們能看出幾點(diǎn)信息:
最后,通過給 absolute元素設(shè)置 top、left 值,可自定義其內(nèi)容,這個(gè)都是平時(shí)比較常用的了。這里需要注意的是,設(shè)置了 top、left 值時(shí),元素是相對(duì)于最近的定位上下文來定位的,而不是相對(duì)于瀏覽器定位。
3. fixed
其實(shí) fixed 和 absolute 是一樣的,唯一的區(qū)別在于:absolute 元素是根據(jù)最近的定位上下文確定位置,而 fixed 根據(jù) window (或者 iframe)確定位置。
題目:relative、absolute 和 fixed 分別依據(jù)誰來定位?
4. 定位上下文
relative 元素的定位永遠(yuǎn)是相對(duì)于元素自身位置的,和其他元素沒關(guān)系,也不會(huì)影響其他元素。
fixed 元素的定位是相對(duì)于 window (或者 iframe)邊界的,和其他元素沒有關(guān)系。但是它具有破壞性,會(huì)導(dǎo)致其他元素位置的變化。
absolute 的定位相對(duì)于前兩者要復(fù)雜許多。如果為 absolute 設(shè)置了 top、left,瀏覽器會(huì)根據(jù)什么去確定它的縱向和橫向的偏移量呢?答案是瀏覽器會(huì)遞歸查找該元素的所有父元素,如果找到一個(gè)設(shè)置了position:relative/absolute/fixed的元素,就以該元素為基準(zhǔn)定位,如果沒找到,就以瀏覽器邊界定位。如下兩個(gè)圖所示:
布局的傳統(tǒng)解決方案基于盒子模型,依賴 display 屬性 + position 屬性 + float 屬性。它對(duì)于那些特殊布局非常不方便,比如,垂直居中(下文會(huì)專門講解)就不容易實(shí)現(xiàn)。在目前主流的移動(dòng)端頁面中,使用 flex 布局能更好地完成需求,因此 flex 布局的知識(shí)是必須要掌握的。
1. 基本使用
任何一個(gè)容器都可以使用 flex 布局,代碼也很簡單。
<style type="text/css">
.container {
display: flex;
}
.item {
border: 1px solid #000;
flex: 1;
}
</style>
<div class="container">
<div class="item">aaa</div>
<div class="item" style="flex: 2">bbb</div>
<div class="item">ccc</div>
<div class="item">ddd</div>
</div>
注意,第三個(gè)<div>的flex: 2,其他的<div>的flex: 1,這樣第二個(gè)<div>的寬度就是其他的<div>的兩倍。
2. 設(shè)計(jì)原理
設(shè)置了display: flex的元素,我們稱為“容器”(flex container),其所有的子節(jié)點(diǎn)我們稱為“成員”(flex item)。容器默認(rèn)存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)。主軸的開始位置(與邊框的交叉點(diǎn))叫做 main start,結(jié)束位置叫做 main end;交叉軸的開始位置叫做 cross start,結(jié)束位置叫做cross end。項(xiàng)目默認(rèn)沿主軸排列。單個(gè)項(xiàng)目占據(jù)的主軸空間叫做 main size,占據(jù)的交叉軸空間叫做 cross size。
將以上文字和圖片結(jié)合起來,再詳細(xì)看一遍,這樣就能理解 flex 的設(shè)計(jì)原理,才能更好地實(shí)際使用。
3. 設(shè)置主軸的方向
flex-direction可決定主軸的方向,有四個(gè)可選值:
.box {
flex-direction: column-reverse| column | row | row-reverse;
}
以上代碼設(shè)置的主軸方向,將依次對(duì)應(yīng)下圖:
4. 設(shè)置主軸的對(duì)齊方式
justify-content屬性定義了項(xiàng)目在主軸上的對(duì)齊方式,值如下:
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
5. 交叉軸的對(duì)齊方式
align-items屬性定義項(xiàng)目在交叉軸上如何對(duì)齊,值如下:
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
題目:如何實(shí)現(xiàn)水平居中?
1. 水平居中
inline 元素用text-align: center;即可,如下:
.container {
text-align: center;
}
block 元素可使用margin: auto;,PC 時(shí)代的很多網(wǎng)站都這么搞。
.container {
text-align: center;
}
.item {
width: 1000px;
margin: auto;
}
絕對(duì)定位元素可結(jié)合left和margin實(shí)現(xiàn),但是必須知道寬度。
.container {
position: relative;
width: 500px;
}
.item {
width: 300px;
height: 100px;
position: absolute;
left: 50%;
margin: -150px;
}
題目:如何實(shí)現(xiàn)垂直居中?
2. 垂直居中
inline 元素可設(shè)置line-height的值等于height值,如單行文字垂直居中:
.container {
height: 50px;
line-height: 50px;
}
絕對(duì)定位元素,可結(jié)合left和margin實(shí)現(xiàn),但是必須知道尺寸。
.container {
position: relative;
height: 200px;
}
.item {
width: 80px;
height: 40px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -20px;
margin-left: -40px;
}
絕對(duì)定位可結(jié)合transform實(shí)現(xiàn)居中。
.container {
position: relative;
height: 200px;
}
.item {
width: 80px;
height: 40px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: blue;
}
絕對(duì)定位結(jié)合margin: auto,不需要提前知道尺寸,兼容性好。
.container {
position: relative;
height: 300px;
}
.item {
width: 100px;
height: 50px;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
其他的解決方案還有,不過沒必要掌握太多,能說出上文的這幾個(gè)解決方案即可。
題目:如何理解 HTML 語義化?
所謂“語義”就是為了更易讀懂,這要分兩部分:
1. 讓人更易讀懂
2. 讓機(jī)器更易讀懂
CSS3 可以實(shí)現(xiàn)動(dòng)畫,代替原來的 Flash 和 JavaScript 方案。
首先,使用@keyframes定義一個(gè)動(dòng)畫,名稱為testAnimation,如下代碼,通過百分比來設(shè)置不同的 CSS 樣式,規(guī)定動(dòng)畫的變化。所有的動(dòng)畫變化都可以這么定義出來。
@keyframes testAnimation
{
0% {background: red; left:0; top:0;}
25% {background: yellow; left:200px; top:0;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0; top:200px;}
100% {background: red; left:0; top:0;}
}
后,針對(duì)一個(gè) CSS 選擇器來設(shè)置動(dòng)畫,例如針對(duì)div元素設(shè)置動(dòng)畫,如下:
div {
width: 100px;
height: 50px;
position: absolute;
animation-name: myfirst;
animation-duration: 5s;
}
animation-name對(duì)應(yīng)到動(dòng)畫名稱,animation-duration是動(dòng)畫時(shí)長,還有其他屬性:
題目:CSS 的transition和animation有何區(qū)別?
首先transition和animation都可以做動(dòng)效,從語義上來理解,transition是過渡,由一個(gè)狀態(tài)過渡到另一個(gè)狀態(tài),比如高度100px過渡到200px;而animation是動(dòng)畫,即更專業(yè)做動(dòng)效的,animation有幀的概念,可以設(shè)置關(guān)鍵幀keyframe,一個(gè)動(dòng)畫可以由多個(gè)關(guān)鍵幀多個(gè)狀態(tài)過渡組成,另外animation也包含上面提到的多個(gè)屬性。
重繪和回流是面試題經(jīng)??嫉念}目,也是性能優(yōu)化當(dāng)中應(yīng)該注意的點(diǎn),下面筆者簡單介紹下。
相比之下,回流要比重繪消耗性能開支更大。另外,一些屬性的讀取也會(huì)引起回流,比如讀取某個(gè) DOM 的高度和寬度,或者使用getComputedStyle方法。在寫代碼的時(shí)候要避免回流和重繪。比如在筆試中可能會(huì)遇見下面的題目:
題目:找出下面代碼的優(yōu)化點(diǎn),并且優(yōu)化它
var data = ['string1', 'string2', 'string3'];
for(var i = 0; i < data.length; i++){
var dom = document.getElementById('list');
dom.innerHTML += '<li>' + data[i] + '</li>';
}
上面的代碼在循環(huán)中每次都獲取dom,然后對(duì)其內(nèi)部的 HTML 進(jìn)行累加li,每次都會(huì)操作 DOM 結(jié)構(gòu),可以改成使用documentFragment或者先遍歷組成 HTML 的字符串,最后操作一次innerHTML。
本小節(jié)總結(jié)了 CSS 和 HTML 常考的知識(shí)點(diǎn),包括 CSS 中比較重要的定位、布局的知識(shí),也介紹了一些 CSS3 的知識(shí)點(diǎn)概念和題目,以及 HTML 的語義化。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。