最近整理了一份HTML/CSS/JS編碼規範,供你們參考。
目錄:
1、HTML編碼規範
2、CSS編碼規範
3、JS編碼規範javascript
根據W3C標準,img標籤要寫alt屬性,若是沒有就寫一個空的。可是通常要寫一個有內容的,根據圖片想要表達的意思,由於alt是在圖片沒法加載時顯示的文字。以下不太好的寫法:css
<img src="company-logo.svg" alt="ABC Company Logo">
更好的寫法:html
<img src="chime-logo.svg" alt="ABC Company">
這裏就不用告訴用戶它是一個Logo了,直接告訴它是ABC Compay就行了。再如:前端
<img src="user-head.jpg" alt="User Portrait">
可改爲:html5
<img src="user-head.jpg" alt="Bolynga Team">
若是圖片顯示不出來,就直接顯示用戶的名字。java
有些人偷懶就直接寫個空的alt那也能夠,可是在一些重要的地方仍是要寫一下,畢竟它仍是有利於SEO.node
爲何?由於寫了也沒用,還顯得你不懂html規範,咱們不是寫XHTML。常見的單標籤有img、link、input、hr、br,以下很差的寫法:jquery
<img src="test.jpg"></img><br/> <input type="email" value=""/>
應改爲:android
<img src="test.jpg"><br> <input type="email" value="">
若是你用React寫jsx模板,它就要求每一個標籤都要閉合,可是它始終不是原生html.ios
本身添加的非標準的屬性要以data-開頭,不然w3c validator會認爲是不規範的,以下很差的寫法:
<div count="5"></div>
應改爲:
<div data-count="5"></div>
以下很差的寫法:
<div> <li></li> <li></li> </div>
更常見的是td沒有寫在tr裏面:
<table> <td></td> <td></td> </table>
若是你寫得不規範,有些瀏覽器會幫你矯正,可是有些可能就沒有那麼幸運。由於標準並無說若是寫得不規範應該怎麼處理,各家瀏覽器可能有本身的處理方式。
有時候可能會直接在ul裏面寫一個div或者span,覺得這樣不要緊:
<ol> <span>123</span> <li>a</li> <li>b</li> </ol>
這樣寫也是不規範的,不能直接在ol裏面寫span,ol是一個列表,它的子元素應該都是display: list-item的,忽然冒出來個span,你讓瀏覽器如何自處。因此寫得不規範就會致使在不一樣的瀏覽器會有不一樣的表現。
一樣,tr的直接子元素都應該是td,你在td裏面寫tr那就亂了。
若是你用了section/aside/article/nav這種標籤的話,須要在裏面寫一個h1/h2/h3之類的標題標籤,由於這四個標籤能夠劃分章節,它們都是獨立的章節,須要有標題,若是UI裏面根本就沒有標題呢?那你能夠寫一個隱藏的標題標籤,若是出於SEO的目的,你不能直接display: none,而要用一些特殊的處理方式,以下套一個hidden-text的類:
<style>.hidden-text{position: absolute; left: -9999px; right: -9999px}</style> <section> <h1 class="hidden-text">Listing Detail</h1> </section>
使用section的好處是能夠劃分章節,以下代碼:
<body> <h1>Listing Detail</h1> <section> <h1>House Infomation</h1> <section> <h1>LOCATION</h1> <p></p> </section> <section> <h1>BUILDING</h1> <p></p> </section> </section> <section> <h1>Listing Picture</h1> </section> </body>
就會被outline成這樣的大綱:
Listing Detail
能夠使用html5 outliner進行實驗,能夠看到,咱們很任性地使用了多個h1標籤,這個在html4裏面是不合法的。
例以下面的寫法是不合法的:
<a href="/listing?id=123"> <div></div> </a>
a標籤是一個行內元素,行內元素裏面套了一個div的標籤,這樣可能會致使a標籤沒法正常點擊。再或者是span裏面套了div,這種狀況下須要把inline元素顯式地設置display爲block,以下代碼:
<a href="/listing?id=123" style="display: block"> <div></div> </a>
這樣就正常了。
設置頁面的渲染模式爲標準模式,若是忘記寫了會怎麼樣?忘記寫了會變成怪異模式,怪異模式下不少東西渲染會有所不一樣,怪異模式下input/textarea的默認盒模型會變成border-box,文檔高度會變成可視窗口的高度,獲取window的高度時就不是指望的文檔高度。還有一個區別,父容器行高在怪異模式下將不會影響子元素,以下代碼:
<div><img src="test.jpg" style="height:100px"></div>
在標準模式下div下方會留點空白,而在怪異模式下會。這個就提醒咱們在寫郵件模板時須要在頂部加上<!DOCType html>,由於在本地開發郵件模板時是寫html片斷,沒有這個的話就會變成怪異模式。
因爲郵件客戶端多種多樣,你不知道用戶是使用什麼看的郵件,有多是用的網頁郵箱,也有可能用的gmail/outlook/網易郵箱大師等客戶端。這些客戶端多種多樣,對html/css的支持也不一,因此咱們不能使用高級的佈局和排版,例如flex/float/absolute定位,使用較初級的table佈局可以達到兼容性最好的效果,而且還有伸縮的效果。
另外郵件模板裏面不能寫媒體查詢,不能寫script,不能寫外聯樣式,這些都會被郵件客戶端過濾掉,樣式都得用內聯style,你能夠先寫成外聯,而後再用一些工具幫你生成內聯html。
寫完後要實際測一下,能夠用QQ郵箱發送,它支持發送html格式文本,發完後在不一樣的客戶端打開看一下,看有沒有問題,如手機的客戶端,電腦的客戶端,以及瀏覽器。
因爲你不知道用戶是用手機打開仍是電腦打開,因此你不能把郵件內容的寬度寫死,可是徹底100%也很差,在PC大屏幕上看起來可能會太大,因此通常能夠這樣寫:
<table style="border-collapse:collapse;font-family: Helvetica Neue,Helvetica,Arial;font-size:14px;width:100%;height:100%"> <tr><td align="center" valign="top"><table style="border:1px solid #ececec;border-top:none; max-width:600px;border-collapse:collapse"> <tr><td>內容1</td></tr> <tr><td>內容2</td></tr> </table></td></tr></table>
最外面的table寬度100%,裏面的table有一個max-width:600px,相對於外面的table居中。這樣在PC上最大寬度就爲600px,而在手機客戶端上寬度就爲100%。
可是有些客戶端如比較老的outlook沒法識別max-width的屬性,致使在PC上太寬。可是這個沒有辦法,由於咱們不能直接把寬度寫死否則在手機上就要左右滑了,也不能寫script判斷ua之類的方法。因此沒法兼容較老版本outlook.
須要套不少層的,通常有兩種狀況,一種是切圖不太會,須要套不少層來維持排版,第二種是會切圖,可是把UI拆解得太細。像如下佈局:
我會這麼寫:
<ul> <li> <div class="img-container"> <img> </div> <div class="text"></div> </li> </ul>
由於它是一個列表,因此外面用ul/li做爲容器,裏面就兩個div,一個圖片的float: left,另一個文字的容器,這樣就能夠了。不用套不少層,可是有一些是必要的,你寫得太簡單的話,擴展性很差,例如它是一個列表那你就應該使用ul/ol,若是你要清除浮動,那你可能須要套一個clearfix的容器。
若是有一塊是總體,它總體向右排版,那麼這一塊也要套一個容器,有時候爲了實現一些效果,可能也要多套一個容器,例如讓外面的容器float,而裏面的容器display: table-cell。可是你套這些容器應該都是有價值,若是你只是爲了在功能上看起來整齊劃一,區分明顯好像沒太大的意義。
一般來講,在html裏面直接寫script和style是一種很差的實踐,這樣把樣式、結構和邏輯都摻雜在一塊兒了。可是有時候爲了不閃屏的問題,可能會直接在相應的html後面跟上調整的script,這種script看起來有點醜陋,可是很實用,是沒有辦法的辦法。
樣式不能寫在body裏,寫在body裏會致使渲染兩次,特別是寫得越靠後,可能會出現閃屏的狀況,例如上面的已經渲染好了,忽然遇到一個style標籤,致使它要從新渲染,這樣就閃了一下,無論是從碼農的追求仍是用戶的體驗,在body裏面寫style終究是一種下策。
一樣地script不要寫在head標籤裏面,會阻礙頁面加載。
而CSS也推薦寫成style標籤直接嵌在頁面上,由於若是搞個外鏈,瀏覽器須要先作域名解析,而後再創建鏈接,接着纔是下載,這一套下來可能已通過了0.5s/1s,甚至2~3秒。而寫在頁面的CSS雖然沒法緩存,可是自己它也不會很大,再加gzip壓縮,基本上在50k之內。
以下,若是是英文的網頁,應該這麼寫:
<html lang="en"> <html lang="en-US">
第一種表示它是英文的網頁,第二種表示它是美國英語的網頁,加上這個的好處是有利於SEO和屏幕閱讀器使用者,他能夠快速地知道這個網頁是什麼語言的,若是是中文能夠這麼寫:
<html lang="zh-CN">
以下,通常charset的meta標籤要寫在head標籤後的第一個標籤:
<head> <meta charset="utf-8"> </head>
一個緣由是避免網頁顯示unicode符號時亂碼,寫在前面是由於w3c有規定,語言編碼要在html文檔的前1024個字節。若是不寫的話在老的瀏覽器會有utf-7攻擊的隱患,具體能夠自行查閱資料,只是如今的瀏覽器基本都去掉了對utf-7編碼的支持了。
charset的標籤寫成html5的這種比較簡潔的寫法就好了,不須要寫成html4這種長長的:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
據我所查,就算是IE6也支持那種簡短的寫法,雖然它不是一個html5瀏覽器。
不要直接把Unicode的特殊符號直接拷到html文檔裏面,要使用它對應的實體Entity,經常使用的以下表所示:
符號 | 實體編碼 |
© | © |
¥ | ¥ |
® | ® |
> | > |
< | < |
& | & |
特別是像©這種符號,不要從UI裏面直接拷一個unicode的字符過去,若是直接拷過去會比較醜,它取的是用的字體裏面的符號。
有時候可能你須要在寫一個空的img標籤,而後在JS裏面動態地給它賦src,因此你可能會這麼寫:
<img src="" alt>
可是這樣寫會有問題,若是你寫了一個空的src,會致使瀏覽器認爲src就是當前頁面連接,而後會再一次請求當前頁面,就跟你寫一個a標籤的href爲空相似。若是是background-image也會有相似的問題。這個時候怎麼辦呢?若是你隨便寫一個不存在的url,瀏覽器會報404的錯誤。
我知道的有兩種解決方法,第一種是把src寫成about:blank,以下:
<img src="about:blank" alt>
這樣它會去加載一個空白頁面,這個沒有兼容問題,不會加載當前頁面,也不會報錯。
第二種辦法是寫一個1px的透明像素的base64,以下代碼所示:
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
第二種可能比較符合規範,可是第一種比較簡單,而且沒有兼容性問題。
有時候換行可能會引入空格,以下代碼:
<form> <label>Email: </label> <input type="email"> </form>
在label和input中間會有一個空格,這樣可能會致使設置lable的width和input 的width二者的和等於form的時候會致使input換行了,有時候你檢查半天沒查出緣由,最後可能發現,原來是多了一個空格,而這個空格是換行引發的。這個時候你可能會有一個問題,爲何<form>和<label>之間以及<input>和</form>之間的換行爲何沒引入空格?這是由於塊級元素開始的空白文本將會被忽略,以下Chrome源碼的說明:
// Whitespace at the start of a block just goes away. Don't even
// make a layout object for this text.
而且,塊級元素後面的空白文本結點將不會參與渲染,也就是說像這種:
<div></div> <div></div>
兩個div之間有textNode的文本節點,可是不會參與渲染。
要注意的是註釋標籤也是正常的頁面標籤,也會給它建立一個相應的節點,只是它不參與渲染。
以下使用-鏈接,不要使用駝峯式:
<div class="hello-world"></div>
是否能夠使用自定義標籤,像angular那樣都是用的自定義標籤,以下代碼:
<my-cotnainer></my-container>
通常不推薦使用自定義標籤,angular也有開關能夠控制是否要使用自定義標籤。雖然使用自定義標籤也是合法的,只要你給他display: block,它就像一個div同樣了,可是無論是從SEO仍是規範化的角度,自定義標籤仍是有點另類,雖然可能你會以爲它的語義化更好。
咱們知道,若是在頁面寫了兩個如出一轍的id,那麼查DOM的時候只會取第一個,同理重複的屬性也會只取第一個,以下:
<input class="books" type="text" name="books" class="valid">
第二個class將會被忽略,className重複了又會怎麼樣?重複的也是無效的,這裏主要是注意若是你直接操做原生className要注意避免className重複,以下代碼:
var input = form.books; input.className += " valid";
若是重複執行的話,className將會有重複的valid類。
例如,若是你要設置一個圖片的寬高,可能這麼寫:
<img src="test.jpg" alt width="400" height="300">
這個在ios的safari上面是不支持的,能夠自行實驗。
或者table也有一些能夠設置:
<table border="1"></table>
可是這種可以用CSS設置的就用CSS,可是有一個例外就是canvas的寬高須要寫在html上,以下代碼:
<canvas width="800" height="600"></canvas>
若是你用CSS設置的話它會變成拉伸,變得比較模糊。
標籤使用上不要太單調:
(1)若是內容是表格就使用table,table有自適應的優勢;若是是一個列表就使用ol/ul標籤,擴展性比較好
(2)若是是輸入框就使用input,而不是寫一個p標籤,而後設置contenteditable=true,由於這個在IOS Safari上光標定位容易出現問題。若是須要作特殊效果除外
(3)若是是粗體就使用b/strong,而不是本身設置font-weight
(4)若是是表單就使用form標籤,注意form裏面不能套form
(5)若是是跳鏈就使用a標籤,而不是本身寫onclick跳轉。a標籤裏面不能套a標籤
(6)使用html5語義化標籤,如導航使用nav,側邊欄使用aside,頂部和尾部使用header/footer,頁面比較獨立的部分能夠使用article,如用戶的評論。
(7)若是是按鈕就應該寫一個button或者<input type=」button」>,而不是寫一個a標籤設置樣式,由於使用button能夠設置disabled,而後使用CSS的:disabled,還有:active等僞類使用,例如在:active的時候設置按鈕被按下去的感受
(8)若是是標題就應該使用標題標籤h1/h2/h3,而不是本身寫一個<p class=」title」></p>,相反若是內容不是標題就不要使用標題標籤了
(9)在手機上使用select標籤,會有原生的下拉控件,手機上原生select的下拉效果體驗每每比較好,無論是IOS仍是android,而使用<input type=」tel」>在手機上會彈一個電話號碼的鍵盤,<input type=」number」> <input type=」email」>都會彈相應的鍵盤
(10)若是是分隔線就使用hr標籤,而不是本身寫一個border-bottom的樣式,使用hr容易進行檢查
(11)若是是換行文本就應該使用p標籤,而不是寫br,由於p標籤能夠用margin設置行間距,可是若是是長文本的話使用div,由於p標籤裏面不能有p標籤,特別是當數據是後端給的,可能會帶有p標籤,因此這時容器不能使用p標籤。
只要https的網頁請求了一張http的圖片,就會致使瀏覽器地址欄左邊的小鎖沒有了,通常不要寫死,寫成根據當前域名的協議去加載,用//開頭:
<img src=」//static.chimeroi.com/hello-world.jpg」>
文件名建議用小寫字母加中橫線的方式。爲何呢?由於這樣可讀性比較強,看起來比較清爽,像連接也是用這樣的方式,例如stackoverflow的url:
https://stackoverflow.com/questions/25704650/disable-blue-highlight-when-touch-press-object-with-cursorpointer
或者是github的地址:
https://github.com/wangjeaf/ckstyle-node
那爲何變量名不用小寫字母加小劃線的方式,如:family_tree,而是推薦用駝峯式的familyTree?C語言就喜歡用這種方式命名變量,可是因爲由於下劃線比較難敲(shift + -),因此通常用駝峯式命名變量的居多。
引入CSS文件的link能夠不用帶type="text/css",以下代碼:
<link rel="stylesheet" href="test.css">
由於link裏面最重要的是rel這個屬性,能夠不要type,可是不能沒有rel。
JS也是一樣道理,能夠不用type,以下代碼:
<script src="test.js"></script>
沒有兼容性問題。
屬性的書寫順序對於瀏覽器來講沒有區別,除了優先級覆蓋以外。可是若是順序保持一致的話,掃一眼能夠很快地知道這個選擇器有什麼類型的屬性影響了它,因此通常要把比較重要的屬性放前面。比較建議的順序是這樣的:
你可能會以爲我平時差很少就是這麼寫的,那麼說明你有一個比較好的素養。而且我以爲規則不是死,有時候能夠靈活,就像你可能會用transform寫居中,而後把left/top/transform挨在一塊兒寫了,我以爲這也是無可厚非的,由於這樣可讓人一眼看出你要幹嗎。
有些人可能喜歡用樣式的特色命名,例如:
.red-font{ color: red; } .p1{ font-size: 18px; } .p2{ font-size: 16px; }
而後你在它的html裏面就會看到套了大量的p1/p2/bold-font/right-wrap之類的類名,這種是不可取的,假設你搞了個red-font,下次UI要改顏色,那你寫的這個類名就沒用了,或者是在響應式裏面在右邊的排版在小屏的時候就會跑到下面去,那你取個right就沒用了。有些人先把UI總體瞄了一下,發現UI大概用了3種字號18px/16px/14px,因而寫3個類p1/p2/p3,不一樣的字號就套不一樣的類。這乍一看,好像寫得挺通用,可是當你看他的html時,你就瘋掉了,這些p1/p2/p3的類加起寫了有二三十個,密密麻麻的。我以爲若是要這樣寫的話還不如藉助標題標籤以下:
.house-info h2{ font-size: 18px; } .house-info h3{ font-size: 16px; }
由於把它的字號加大了,極可能是一個標題,因此爲何不直接用標題標籤,不能僅僅擔憂由於標題標籤會有默認樣式。
類的命名應當使用它所表示的邏輯意義,如signup-success-toast、request-demo、agent-portrait、 company-logo等等。
若是有些樣式你以爲真的特別通用,那能夠把它看成一個類,如clearfix,或者有些動畫效果,有幾個地方都要用到,我以爲這種較爲複雜而且通用的能夠單獨做爲一個類。可是仍是趨向於使用意義命名。
有些人在寫CSS的時候使用一些hack的方法註釋,以下:
.agent-name{ float: left; _margin: 10px; //padding: 20px; }
這種方法的原理是因爲//或者_開頭的CSS屬性瀏覽器不認識,因而就被忽略,分號是屬性終止符,從//到分號的內容都被瀏覽器忽略,可是這種註釋是不提倡的,要麼把.css文件改爲.less或者.scss文件,這樣就能夠愉快地用//註釋了。
還有一些專門針對特定瀏覽器的hack,如*開頭的屬性是對IE6的hack。無論怎麼樣都不要使用hack。
選擇器通常不要寫超過3個,有些人寫sass或者less喜歡套不少層,以下:
.listings-list{ ul{ li{ .bed-bath{ p{ color: #505050; } } } } }
一個容器就套一層,一層一層地套下來,最底層套了七八層,這麼長的選擇器的性能比較差,由於Chrome裏面是用遞歸從最後一個選擇器一直匹配到第一個,選擇器越多,匹配的時間就越長,因此時間會比較長,而且代碼的可讀性也比較差,爲看到最裏面的p標籤的樣式是哪一個的我得一層層地往上看,看是哪裏的p。代碼裏面縮進了七、8層看起來也比較累。
通常只要寫兩三個比較重要的選擇器就行了,不用每一個容器都寫進去,重要的目標元素套上class或者id。
最後一個選擇器的標籤的應該少用,由於若是你寫個.container div{}的話,那麼頁面上全部的div第一次都匹配中,由於它是從右往左匹配的,這樣的寫的好處是html不用套不少的類,可是擴展性很差,因此不要輕易這樣用,若是要用須要仔細考慮,若是合適才使用,最起碼不能濫用。
有時候會出現本身的樣式受到其餘人樣式的影響,或者本身的樣式不當心影響了別人,有多是由於類的命名和別人同樣,還有多是選擇器寫的範圍太廣,例若有人在他本身的頁面寫了:
* {
box-sizing: border-box; }
結果致使在他個頁面的公用彈框樣式掛了。一方面不要寫*全局匹配選擇器,無論從性能仍是影響範圍來講都太大了,例如在一個有3個子選擇器的選擇器:
.house-info .key-detail .location{}
在三個容器裏面,*都是適用的,而且有些屬性是會繼承的,像font-size,會致使這三個容器都有font-size,而後一層層地覆蓋。
還有一種狀況是濫用了:first-child、:nth-of-type這種選擇器,使用這種選擇器的後果是擴展性很差,只要html改了,就會致使樣式無論用了,或者影響到了其它無關元素。可是並非說這種選擇器就不能用,只要用得好仍是挺方便的,例如說在全部的li裏面要讓最後一個li的margin-left小一點,那麼能夠這麼寫:
.listing-list li:last-child{ margin-left: 10px; }
這可能比你直接套一個類強。可是無論怎麼樣,不能濫用,合適的時候才使用,而不是僅僅爲了少寫類名。
覆蓋是一種經常使用的策略,也是一種不太優雅的方式,以下代碼,爲了讓每一個house中間的20px的間距,可是第一個house不要有間距:
.house{ margin-top: 20px; } .house:first-child{ margin-top: 0; }
其實能夠改爲這樣:
.house + .house{ margin-top: 20px; }
只有前面有.house的.house才能命中這個選擇器,因爲第一個.house前面沒有,因此命不中,這樣看起來代碼就簡潔多了。
還有這種狀況,以下代碼所示:
.request-demo input{ border: 1px solid #282828; } .request-demo input[type=submit]{ border: none; }
其實能夠藉助一個:not選擇器:
.request-demo input:not([type=sbumit]){ border: 1px solid #282828; }
這樣看起來代碼也優雅了不少。
有一種覆蓋是值得的,那就是響應式裏面小屏的樣式覆蓋大屏,以下:
.container{ width: 1080px; margin: 0 auto; } @media (min-width: 1024px){ .container{ width: auto; margin: 0 40px; } }
大屏的樣式也能夠寫成:
@media (min-width: 1025px){ .container{ width: 1080px; margin: 0 auto; } }
我一開始是就是這麼寫的,爲了遵循減小覆蓋原則,可是後來發現這種實踐很差,代碼容易亂,寫成覆蓋的好處在於能夠在瀏覽器明顯地看到,小屏的樣式是覆蓋了哪一個大屏的樣式,這個在大屏的時候又是怎麼樣的。
上面提到:not可讓代碼簡潔,還有其它一些很好用的。例如說只有兩個的時候一個佔比50%,而有3個的時候一個佔比33%,這個用CSS就能夠實現,以下:
.listing-list li{ width: 33%; } .listing-list li:first-child:nth-last-child(2), .listing-list li:first-child:nth-last-child(2) ~ li{ width: 50%; }
當li是第一個元素而且是倒數第二個元素的時候以及和它相鄰的li被第二組選擇器命中,它的寬度是50%,也就是隻有兩個li的時候才能知足這個條件。
另外還能夠借用:hover/:focus/:invalid/:disabled等僞類選擇器完成一些簡單的交互。
important用來覆蓋屬性,特別是在CSS裏面用來覆蓋style裏的屬性,可是important仍是少用爲妙。有時候你爲了偷懶直接寫個!important,覺得這個的優先級是最高的了,每每螳螂捕蟬,黃雀在後,極可能過不了多久又要再寫一個優先級更高的覆蓋掉,這樣就略顯尷尬了。因此能少用仍是少用。若是要覆蓋仍是先經過增長添加選擇器權重的方式。
"程序猿最煩兩件事,第一件事是別人要他給本身的代碼寫文檔,第二件呢?是別人的程序沒有留下文檔"。註釋也是一樣道理,當看到不少綠色的註釋代碼神經會比較放鬆,而當看到揉成一團尚未註釋的代碼是比較壓抑的。CSS的註釋可包括:
/* * @description整個列表頁樣式入口文件 * @author yincheng.li */
/*詳情頁貸款計算器*/
/*爲了去除輸入框和表單點擊時的灰色背景*/ input, form{ -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
有時候你看源碼的時候你會看到一些TODO的註釋:
/* TODO(littledan): Computed properties don't work yet in nosnap. Rephrase when they do. */
表示這些代碼還有待完善,或者有些缺陷須要之後修復。而這種TODO的註釋通常編輯器會把TODO高亮。
注意不要寫一些錯誤的誤導的註釋或者比較廢話的註釋,這種還不如不寫,以下:
/* 標題的字號要大一點 */ .listings h2{ font-size: 20px; }
無論是JS/CSS,縮進都調成4個空格,若是你用的sublime,在軟件的右下角有一個Tab Size,選擇Tab Size 4,而後再把最上面的Indent Using Spaces勾上,這樣,當你打一個tab鍵縮進的時候就會自動轉換成4個空格。若是你使用vim,新增或者編輯~/.vimrc文件新增一行:
:set tabstop=4
也會自動把縮進改爲4個空格,其它編輯器自行查找設置方法。由於\t在不一樣的編輯器上顯示長度不同,而改爲空格能夠在不一樣人的電腦上格式保持一致。
多個選擇器共用一個樣式集,每一個選擇器要各佔一行,以下:
.landing-pop, .samll-pop-outer, .signup-success{ display: none; }
每一個屬性名字冒號後面要帶個空格,~、>、+選擇器的先後也要帶一個空格:
.listings > li{ float: left; }
例如:
.list{ border: 1px solid 0px; margin: 0px; }
應改爲:
.list{ border: 1px solid 0; margin: 0; }
可是有個特例,就是和時間有關的時間單位都要帶上秒s,以下兩個都是不合法的:
transition-duration: 0; transition: transform 0 linear;
以下:
color: rgb(80, 80, 80);
應改爲:
color: #505050;
由於使用rgb是一個函數,它還要計算一下轉換。若是是帶有透明度的再用rgba.
若是色值的六個數字同樣,那麼寫3個就好:
color: #ccc;
以下兩個意思同樣:
border: 0; border-width: 0;
而下面這兩個同樣:
border: none; border-style: none;
因此用0和none均可以去掉邊框。
你可能會說打包工具其實最後會幫我處理,但本身要保持一個良好的書寫習慣仍是很重要的。
注意使用系統字體的對應的font-family名稱,如SFUIText Font這個字體,在Safari是-apple-system,而在Chrome是BlinkMacSystemFont,因此font-family能夠這麼寫:
font-family{ font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
再如微軟雅黑,不少中文網站都用這個字體,要寫成:
font-family{ font-family: Microsoft YaHei; }
另外font-family不能在代碼任意設置,若是使用了自定義字體。以下代碼:
.title{ font-family: Lato Bold; }
由於若是你在代碼裏面寫了好多個font-family,到時候要總體替換網頁的字體就很麻煩了,正確的作法應該是這樣的:
h1, strong, b{ font-family: Lato Bold; font-weight: normal; }
若是須要加粗就用標題標籤,或者b/strong標籤,而且要把font-weight調回來,由於那個字體自己就有加粗效果了,若是font-weight再是粗體的話瀏覽器會用本身的算法繼續加粗。若是是細體怎麼辦,一方面通常細體用得比較少,另外一方面沒有細體的標籤,能夠經過套類的方式。
有些人喜歡設置z-index很大:
z-index: 99999;
覺得他是老大了,不會有人再比他高了,可是螳螂捕蟬,黃雀在後,很快得再寫一個:
z-index: 999999;
一般本身頁面的業務邏輯的z-index應該保持在個位數就行了。
通常的說法是說爲了提升性能,屬性要合併,但其實Chrome每一個屬性都是單獨的,就算你合在一塊兒,它也會幫你拆出來,如把margin拆成left/right/top/bottom,可是咱們仍是推薦寫成合的,由於它可讓代碼看起來更簡潔,代碼量更少,以下代碼:
.container{ margin-top: 20px; margin-left: 10px; margin-right: 10px; }
能夠寫成:
.container{ margin: 20px 10px 0; }
可是合在一塊兒寫了,要注意別覆蓋了其它的設置,如上面把margin-bottom設置成了0.
再如:
.banner{ background-image: url(/test.jpg); background-position: 0 0; background-repeat: no-repeat; }
能夠改爲:
.banner{ background: url(test.jpg) 0 0 no-repeat; }
以下代碼:
a.btn { float: left; display: block; width: 100px; height: 30px; }
第二行的display: block實際上是沒用的,由於若是你浮動了,目標元素就會具備塊級盒模型的特性,即便你display: table-cell或者inline也無論用。若是你是display: flex,那麼float將會被忽略。
一樣地,absolute定位和fixed定位也有一樣的效果,會把行內元素變成塊級的。
清除浮動有多種方法,通常用clearfix大法,雖然這個方法有缺陷,可是它比較簡單且可以適用絕大多數的場景,一個兼容IE8及以上的clearfix的寫法:
.clearfix:after{ content: ""; display: table; clear: both; }
就不要在末尾添加一個多餘元素的方法清除浮動了,雖然也可行,可是比較low.
通常來講font-family不須要添加引號,即便字體名稱帶有空格也不要緊,可是有一種狀況是必定要加上引號的,就是字體名稱恰好是關鍵詞,以下字體都須要加關鍵詞:
font-family: "inherit", "serif", "sans-serif", "monospace", "fantasy", and "cursive"
background-url: url("//cdn.test.me/test.jpg");
你不加也能夠,可是有一種必定要加,那就是url裏面帶有特殊字符沒有轉義,以下:
background-url: url(//cdn.test.me/hello world.jpg)
上面瀏覽器會去加載//cdn.test.me/hello,而後報404。這種狀況一般是圖片是用戶上傳的,圖片的名字帶有空格,後端給的url沒有對特殊字符作處理,就會有問題,因此當url是可變的時候,最好仍是帶上引號:
background-url: url('//cdn.test.me/hello world.jpg');
這樣瀏覽器就能正常加載圖片了。這種狀況最好的仍是從源頭上避免,但咱們也能夠作個兼容。
這兩個都是合法的,只是統一一下比較好,不能一會兒單引號,一會兒雙引號的,比較廣泛的推薦是html使用雙引號,css使用單引號。
(1)不要使用all屬性作動畫
使用transition作動畫的時候不要使用all全部屬性,在有一些瀏覽器上面可能會有一些問題,以下:
transition: all 2s linear;
在Safari上面可能會有一些奇怪的抖動,正確的作法是要用哪一個屬性作動畫就寫哪一個,若是有多個就用隔開,以下代碼所示:
transition: transform 2s linear, opacity 2s linear;
若是能用transform作動畫的,就不會使用left/top/margin等,由於transform不會形成重繪,性能要比position那些高不少,特別是在移動端的時候效果比較明顯。基本上位移的動畫都能用transform完成,不須要使用CSS2的屬性,如一個框從右到左彈出。
例如把一個框,從下到上彈出,能夠用jQuery的slideUp函數,或者本身寫setInterval函數處理,可是這些沒有比用CSS來得好。使用CSS,初始狀態能夠把框translate移動屏幕外,而後點擊的時候加上一個類,這個類的transform值爲0,而後再用transition作動畫就行了。
英文的單詞或者數字若是當前行排不下會自動切到下一行,這樣就致使每行長短不一,有時候可能不太美觀,可是不能使用word-break: break-all把一個單詞拆成兩行,還有一種是使用:
hyphens: auto;
它會把單詞拆成用-鏈接的形式,看起來好像挺合理,可是因爲它斷詞斷得不夠完全,有些單詞斷不了,長短不一的現象看起來也比較明顯,有些單詞還被拆成了兩行,因此還不如不加。
所以,不要使用斷詞。
這個和上面提到的font-family設置是同樣的,不要在代碼裏面手動設置font-family,以下:
.icon-up:before{ content: "\e950"; font-family: "icon-font"; }
正確的作法是給.icon-up的元素再套一個.icon的類,font-family等對圖標字體的相關設置都統一在這個類裏面:
.icon{ font-family: "icon-font"; /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
由於咱們可能會添加其它一些設置,有個.icon的類統一處理比較好。就不要手動一個個去設置font-family了。
因爲每一個瀏覽器都有本身的UA樣式,而且這些樣式還不太統一,因此須要作樣式reset,常見的reset有如下:
/* IE瀏覽器對輸入控件有本身的font-family,須要統一 */ input, textarea, button{ font-family: inherit; } /* Chrome瀏覽器會在輸入控制彙集的時候添加一個藍色的outline*/ input:focus, textarea:focus, select:focus{ outline: none; } /* 去掉textarea的可拉大小功能*/ textarea{ resize: none; } /* IOS Safari在橫屏的時候會放大字體,第二個屬性讓滑動更流暢 */ html{ -webkit-text-size-adjust: 100%; -webkit-overflow-scrolling : touch; } /* 統一標籤的margin值和p標籤的line-height*/ body, p, h1, h2, ul, ol, figure, li{ padding: 0; margin: 0; } h1, h2, p{ line-height: 150%; } /* 去掉select的默認樣式 */ select{ -webkit-appearance: none; } /* 若是有輸入內容IE會給輸入框右邊加一個大大的X */ input::-ms-clear{ display: none; width: 0; height: 0; } /* 去掉number輸入框右邊點擊上下的小三角 */ input::-webkit-inner-spin-button{ -webkit-appearance: none; } input::-webki-outer-spin-button{ -webki-appearance: none; }
無論是UI直接給的圖片仍是本身從UI圖裏切出來的圖片,都須要把圖片壓縮一下,建議使用tinypng,它能夠在保持圖片質量減小較低的狀況下,把圖片壓得很厲害,比直接在PS裏面設置壓縮質量要強。若是是色彩比較豐富的圖片要使用jpg格式,不能使用png格式,png會大得多,若是是logo那種矢量圖片,直接使用svg格式便可。通常來講要把圖片控制在300k之內,特別是banner頭圖,圖片的大小也要控制住。
顯示一張圖片有兩種方式,能夠經過設置CSS的background-image,或者是使用img標籤,究竟何時用哪一種呢?
若是是頭圖等直接展現的圖片仍是要img標籤,若是是作爲背景圖就使用background,由於使用img能夠寫個alt屬性加強SEO,而背景圖那種自己不須要SEO。雖然background有一個一個background-position: center center很好,可是頭圖那種仍是使用img吧,本身去居中吧,否則作不了SEO。
響應式開發最很差不要雜合使用rem,文字字號要麼所有使用rem,要麼不要用,也不要使用transform: scale去縮小,由於被縮小的字號看起來會有點奇怪,別人都是14px,而你變成了13.231px,小了一點。響應式的原則通常是保持中間或者兩邊間距不變,而後縮小主體內容的寬度。
:before和:after能夠用來畫頁面的一些視覺上的輔助性元素,如三角形、短的分隔線、短豎線等,能夠減小頁面上沒有用的標籤。可是頁面上正常的文本等元素仍是不要用before/after畫了。
首先absolute定位的元素渲染性能會比較高,由於它獨立出來了,計算量會少,用得好仍是能夠的。可是若是你頁面的主要佈局是使用absolute的那確定是不可取的,由於absolute定位的可擴展性不好,你把每一個元素的位置都定死了就變不了了,能夠多用float,雖然float的性能相對較差,可是無論是實用性仍是兼容性都是挺好的。
有些人喜歡用inline-block,特別是剛開始學切圖的人,由於block會換行,而inline-block不會換行還具備盒模型,所以inline-block用得很順手,而float比較複雜,還要處理清除浮動之類的問題。以下佈局:
應該寫li,而後讓li float,若是你讓li display:inline-block也能夠達到目的。可是inline-block用得多了可能會有一些奇怪的問題,你一般要在一個inline-block的元素裏面套block的元素,inline-block是行內元素,而block是塊級元素,這二者終究有點差異。這種應該用float/flex會更天然,若是你float用順手了你會發現比inline-block好多了,而且更加專業。若是你沒怎麼用過flex ,那你能夠嘗試換一下使用flex,若是你沒怎麼用過float,能夠嘗試用一下。只有你的切圖方式多樣化了,你切起圖來才能比較靈活。
通常來講,UI給的圖片展現寬高是固定的,可是實際的圖片長寬是不固定,大部分圖片是長是比寬大,小部分圖片是寬比長大。因此須要居中裁剪展現,以下圖所示:
中間黑色的框是展現區域,圖片的短邊和窗器的邊同樣大,另外一邊按圖片的原始比例拉伸,而後居中顯示。這個得藉助JS,由於圖片未加載好以前,不知道是長邊比較大仍是寬比較大。以下代碼:
<div class="img-container"> <img src="test.jpg" alt onload="resizeImg(this, '400px', '300px'"> </div>
藉助一個resizeImg函數,在onload函數裏面作處理。而後居中用CSS:
.img-container{ position: relative; width: 400px; height: 300px; } .img-container img{ position: absolute; left: -9999px; right: -9999px; top: -9999px; bottom: -9999px; margin: auto; }
上面代碼用了一個margin: auto作居中。
移動端的的一些圖標如X,可能會設計得比較小,因此點起來會不太好點,所以要提升可點區域範圍,可經過增長padding,以下代碼:
.icon-close{ position: abosulte; right: 0; top: 0; padding: 20px; }
這樣區域就增長了一圈,點起來就容易多了。
若是設置input的line-height,以下代碼,你可能要作垂直居中:
.request-demo input{ height: 40px; line-height: 40px; }
設置了line-height爲一個很高的值,這樣會致使Safari瀏覽器的輸入光標|變得巨大,因此若是你要居中的話,使用padding吧。
由於IOS Safari切換輸入框的時候會頁面會彈閃得很厲害,由於你在切的時候它會先把鍵盤收起來,而後再彈出來,這個時間很短,給人感受頁面彈閃了一下,但若是把body禁止滑動了就不會有這個問題,這有兩個解決辦法,第一種是把body fixed住,第二種設置body overflow: hidden,相對來講第二種比較簡單一點。IOS10徹底不會閃,IOS9如下仍是會閃。
有時候UI裏面會有一些漸變的效果,沒法複製CSS出來,這個時候能夠用一個在線的工具,生成漸變的CSS:www.cssmatic.com/gradient-ge…,可是這個須要本身手動調一個和UI如出一轍的效果,或者能夠直接給UI調一個它理想的效果,它會生成兼容性很強的CSS:
background: #fff; background: -moz-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: -webkit-gradient(left top, right top, color-stop(0%, #fff), color-stop(43%, #d2d2d2), color-stop(58%, #d1d1d1), color-stop(100%, #fefefe)); background: -webkit-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: -o-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: -ms-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: linear-gradient(to right, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff', endColorstr='#fefefe', GradientType=1 );
以下有些人爲了把span撐開,設置span display: inline-block:
span.phone-numer{ display: inline-block; margin-left: 10px; }
其實行內元素可直接margin的左右,可以把它撐開,不須要設置inline-block:
span.phone-numer{ margin-left: 10px; }
另外須要注意的是img/input/textarea/button默認就是inline-block,也不用再設置。
《代碼大全》這本書裏面有一章是專門講變量命名的,這裏結合這本書的建議作說明。總地來講,變量名要準確完整地描述該變量所表述的事物,具體來講:
如如下好的變量名和很差的變量名:
很差的變量名 | 好的變量名 |
inp | input, priceInput |
day1, day2, param1 | today, tomorrow |
id | userId, orderId |
obj | orderData, houseInfos |
tId | removeMsgTimerId |
handler | submitHandler, searchHandler |
左邊的變量名都不太清楚,代碼的擴展性很差,一旦代碼須要加功能的話,就容易出現obj一、obj二、obj3這種很抽象的命名方式。因此一開始就要把變量的名字起得真實有意義,不要搞一些很短很通用的名字。
固然變量名取得太長也很差,如maximumNumberOfTeamMembers.
變量名應直指問題領域,來源於現實世界,而不是計算機世界,例如取了texareaData之類的名字,應該取一個和業務相關的名字,如leaveMsg.
如up/down、begin/end、opened/closed、visible/invisible、scource/target,對仗明確可讓人很清楚地知道兩個變量的意義和用途。
有些喜歡取temp和obj之類的變量,若是這種臨時變量在兩行代碼內就用完了,接下來的代碼就不會再用了,仍是能夠接受的,如交換數組的兩個元素。可是有些人取了個temp,接下來十幾行代碼都用到了這個temp,這個就讓人很困惑了。因此應該儘可能少用temp類的變量,以下代碼:
var temp = 10; var leftPosition = currentPosition + temp, topPosition = currentPosition - temp;
應改爲:
var adjustSpace = 10; var leftPosition = currentPosition + adjustSpace, topPosition = currentPosition - adjustSpace;
《代碼大全》這本書建議布爾變量不用以is/do之類的開頭,如:
var isMobile = true, isError = true, doUpdate = false;
可改爲:
var mobile = true, error = true, updated = false;
還有其它一些經常使用的名稱如done/found/successs/ok/available/complete等,結合具體的語境:
var ajaxDone = true, fileFound = false, resourceUpdated = true;
另外變量名不要使用否認的名詞,如notOk,notReady,由於否認的詞取反的時候就會比較奇怪,如if(!notOk). 要使用確定的布爾變量名。若是是參數的話可結合ES6的默認形參值。
(6)變量名使用正確的語法
不要使用中文拼音,如shijianchuo應改爲timestamp,若是是複數的話加s,或者加上List,如orderList、menuItems,而過去式的加上ed,如updated/found等,若是正在進行的加上ing,如calling.
以下聲明三個變量:
var registerForm, question, calculateResult;
以上絕對是合法JS語法,可是這三個變量的用途會讓人比較困惑,特別是中間第二個question,問題是什麼。可是當你把上面的變量賦一個初始值的時候:
var registerForm = null, question = "", calculateResult = 0;
就讓人豁然開朗了,原來question是一個問題的字符串,而result是一個數字,form是一個對象。這也有利於JS解釋器提早作一些優化處理,不用等到使用的時候才知道這些變量是什麼類型的。
以下代碼:
function calculatePrice(seatCount){ if (seatCount <= 0) { return ""; } else { return seatCount * 79; } }
這個代碼可能返回整型,也有可能返回字符串,就會讓人比較困惑,同時從代碼性能來講也是不高的,雖然它是合法的JS語法,一個函數的返回類型要統一。你可能會說我用上面的函數作爲輸入框顯示的值,若是是負數或者0,那麼輸入框就不要顯示任何東西,因此纔會返回空的字符串。即便是這樣的緣由也不建議這樣寫,從長遠來看這樣寫是不利的,你應該用其它的方法組織你的代碼。要養成強類型的代碼風格,這樣不容易出bug,擴展也容易。另外若是一個變量你把它當成數字使用,下面就不要再把它當成字符串使用了,由於這樣也容易讓人困惑。微軟的Typescript就是一種強類型的書寫語法,不少大型項目會使用typescript寫JS,有興趣的能夠繼續瞭解怎麼寫typescript.
undefined表示一個變量未定義,你定義了一個變量又說它未定義自己就很奇怪。這可能會形成的問題是使用上的歧義,由於咱們常用undefined來判斷變量有沒有定義:
if (typeof window.HTMLVideoElement === "undefined")
若是要賦值應該要賦空值,如對象賦值爲null,數字賦值爲0,字符串賦值爲空字符,那你可能會說0也是一個正常的數字,若是賦值爲0會致使我誤認爲它是一個正常的數據,那怎麼辦呢?若是你的數字都是非負數,那麼能夠把初始值置爲-1,實在不行就置成NaN.
函數的返回值也不要顯式地return undefined.
一個比較流行的空格和縮進排版以下代碼所示:
//逗號後面帶個空格,) {中間帶個空格 function getSeatDiscount(seatCount, currentPrice) { //雙目運算符左右兩邊都帶個空格 var originPrice = editOrder.getSeatsPrice(seatCount); return Math.round((originPrice - currentPrice) / originPrice * 100); }
一行太長要換行,如V8的源碼裏面一行最長是70個字符,超過就換行:
function ArrayUnshift(arg1) { // length == 1 //if判斷裏面進行了換行,而且if (中間帶個空格 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) && !%object_is_sealed(array)) { SparseMove(array, 0, 0, len, num_arguments); } else { SimpleMove(array, 0, 0, len, num_arguments); } }
一行代碼太長了就換行是一種好的習慣,太長讓人看起來比較費勁。基本上一行不要超過100個字符,超過就要換行,無論是註釋仍是代碼。
==會帶上類型轉換,這和上面同樣的,咱們要用強類型的風格寫代碼,因此不要使用==,若是有類型轉換本身作類型轉換,不要讓別人去猜這裏面有類型轉換,使用==會有一些比較奇怪的結果:
null == undefined //true '' == '0' //false 0 == '' //true 0 == '0' //true ' \t\r\n ' == 0 //true new String("abc") == "abc" //true new Boolean(true) == true //true true == 1 //true
對一些比較重要的常量起一個名字,例以下面的代碼:
const ONE_DATE = 3600 * 24 * 1000; var tomorrow = today + ONE_DATE;
再以下面很差的寫法:
dialogHandler.showQuestionNaire("seller", "sell", 5, true);
上面四個常量會讓人看起來比較困惑,若是能夠的話給它們起個名字,若是以爲麻煩那就加上註釋。
一個緣由是在全局做用域下,變量的查找時間會更長,第二個緣由是污染全局做用域,有時候會形成一些意想不到的結果,以下:
var name = "hi boy"; console.log(window.name);
定義了一個變量,可是恰好不巧window.name是原本有這個屬性,這個屬性一般用來跨域傳遞數據。若是你設置了name這個變量,就把全局的window.name給覆蓋了。
ES6新增了let/const定義變量,使用let有一些好處,如:
let me = "go"; // Uncaught SyntaxError: Identifier 'me' has already been declared let me = "go";
使用babel loader打包的時候它會作靜態檢查:
Module build failed: Duplicate declaration "me"
for(let i = 0; i <= 4; i++) { tasks.push(function(){ console.log("i is " + i); }); }
使用let使得i在for循環裏面每次運行的做用域都是獨立的。而且for裏定義的變量在for循環外是不可見的。
babel在轉換的時候,會在for循環裏面套一個function,而後把i看成函數的參數:
var _loop = function _loop(_i) { tasks.push(function () { console.log("i is " + _i); }); }; for (var _i = 0; _i <= 4; _i++) { _loop(_i); }
因爲let能夠避免變量重複定義,就衝着這一點,就使得它頗有意義。因此推薦多用let定義變量。因此本規範下面的變量將使用let代替var.
而const適合於給常量起個名字,如上面提到的:
const ONE_DAY = 3600 * 24 * 1000; const adjustSpace = 10;
或者是定義其它一些不須要修改的變量,防止不當心被其它代碼修改了。
能夠寫一行就不要寫三行,以下:
let seatDiscount = 100; if(seat < 5) { seatDiscount = 90; } else if(seat < 10) { seatDiscount = 80; } else { seatDiscount = 70; }
能夠改爲三目運算符:
let seatDiscount = seat < 5 ? 90 : seat < 10 ? 80 : 70;
代碼從8行減小到了2行。
例如如下代碼:
setTimeout(function(){ window.location.reload(true); }, 2000);
可改爲:
setTimeout(() => window.location.reload(true), 2000);
代碼從3行變成了1行。
對於通常的頁面的數據量來講,加減乘除等計算不足以形成性能瓶頸。容易形成瓶頸的是DOM操做,特別是大批量的DOM操做,只要一次有幾百上千的級別就容易形成頁面卡頓。特別是不要在一個for循環裏不斷地修改DOM,以下代碼:
for(var i = 0; i < 1000; i++) { ul.appendChild(li); }
這種能夠先把li拼好了,再一次性append到ul裏面,以下代碼:
var fragment = document.createDocumentFragment(); for(var i = 0; i < 1000; i++) { fragment.appendChild(li); } ul.appendChild(fragment);
若是你用jq的話應該先把模板渲染好,而後再一次性append到dom裏面,而不是不斷地append到dom裏面。如今的瀏覽器通常也比較智能,它會作一些優化,可是咱們不能總是期望瀏覽器會優化。
可是仍是要注意數據量特別大的狀況,你可能要使用setTimeout的方式分段處理數據,甚至使用多線程。使用setTimeout能夠這樣:
function sliceWorks(data, finishedCallback) { if(!data.length) { finishedCallback(); } else { const PIECES = 100; process(data.splice(0, PIECES)); setTimeout(() => sliceWorks(data, finishedCallback), 100); } }
咱們使用一個遞歸,把數據分段處理,每段100個,當數據處理完再調完成回調函數。
這個和CSS規範相似:
/* * @file listing-detail.js * @description 房源詳情頁的JS主文件,處理輪播、房貸計算器、約看房等邏輯 * @author yincheng.li * @update (yincheng.li 2017/8/19) */
/* * 和搜索界面展現有關的處理邏輯 * @namespace */ var searchWinHandler = { /* * 初始化驅動函數 * * @param {bool} realTimeSearch 是否須要進行實時搜索 * @param {HTMLFormElement} form 搜索表單DOM元素 * */ init(realTimeSearch, HTMLFormElement){ } /* * 搜索條件展現點擊X按鈕的處理函數 * * @param {object} jquery的點擊事件event * @trigger 會觸發search按鈕的點擊事件,以觸發搜索 * @returns 無返回 * * TODO 這裏臨時使用了一個全局變量的flag,這種實現方式不太好 * 雖然比較方便 */ closeFilterSpan(event){ } };
上面的@auhor @return都是註釋標籤,其它經常使用的註釋標籤還有:
/* @class 表示一個類 @constructor 構造函數 @deprecated 被棄用 @global 全局的變量 @namespace 具備命名空間做用的object,如$.fn.remove,$.fn.append,$和fn就是一個namespace,而fn是$的子命名空間 @this 這裏的this指向哪裏 @throws 在這個函數裏面可能會拋出什麼異常 @version 當前版本 */
對一些比較重要的變量加註釋,標明它是什麼用途,以及對一些核心代碼邏輯加上註釋,或者比較複雜的業務邏輯,寫了5個case,每一個case分別表明什麼;爲了改某個bug而加入的代碼,說明下爲了解決什麼問題;還有某些易混的判斷,爲何if判斷條件寫了四個,爲何代碼到這個if判斷不經過就直接return了;一些常量的註釋,爲何會忽然冒出來100這個數字;改動了別人的代碼,爲何要改動;等等。如:
var requestData = { listingId: listingData.listingId, page: 1, //把200改爲5,點擊More的時候是從新刷新頁面的,也沒有其餘地方用到, //不必請求那麼多,嚴重影響性能 pageSize: 5//200 };
總之多寫註釋仍是好的,只要不是廢話:
//定義了一個number的變量 let number = 5;
或者是和邏輯不符合的錯誤註釋。
還有一種排版的註釋,右括號的對應關係:
} //function ajax } //switch(b) } //if(a) } //searchHandler
主要是爲了方便在後面加代碼,例如我要在switch(b)後面加代碼,當我看到這個註釋我就很清楚地知道須要在哪裏按回車。不過通常不推薦嵌套很深的代碼,或者寫得很長,一個函數幾百行。
有些人的代碼常常會套個七八層,以jq代碼爲例,以下:
var orderHandler = { bindEvent: function(){ $(".update-order").on("click", function(){ if(orderStatus === "active"){ ajax({ url: "/update-order", success: function(data){ for(let i = 0; i < data.orders.length; i++){ dom.append(); } } }); } else { ajax({ url: "/create-order", success: function(data){ } }); } }); } };
上面的代碼最深的一層縮進了八層,你可能會以爲這樣邏輯挺清晰的啊,可是這種寫法同時也有點麪條式。以上代碼若是讓我寫,我會這麼組織:
var orderHandler = { sendUpdateOrderReq: function(requestUrl, successCallback){ ajax({ url: requestUrl, success: successCallback; }); }, updateOrder: function(event){ let requestUrl = orderStatus === "active" ? "/update-order" : "create-order"; //更新訂單回調函數 let activeUpdateCallback = function(data){ for(var i = 0; i < data.orders.length; i++){ console.log(data.orders[i].id); } }; //建立訂單回調函數 let inactiveUpdateCallback = function(data){ }; let successCallback = { active: activeUpdateCallback, inactive: inactiveUpdateCallback }; //發請求處理訂單 searchHandler.sendUpdateOrderReq(requestUrl, successCallback[orderStatus]); }, bindEvent: function(){ $(".update-order").on("click", searchHandler.updateOrder); } };
首先把綁定的匿名函數改爲有名的函數,這樣有個好處,當你想要off掉的時候隨時可off掉,而後能夠減小一層縮進,接着把根據orderStatus不一樣的回調先用變量判斷好,而不是同時積壓到後面再一塊兒處理。再把發送請求的函數再單獨抽出來作爲一個函數,這樣能夠減小兩層縮進。上面最深的縮進爲4層,減小了一半。而且你會發現這樣寫代碼邏輯會更加清晰,我在bindEvent裏面掃一眼就能夠知道哪些DOM綁了哪些事件,而後我對如對哪一個DOM的事件感興趣再跳到相應的回調函數去看,而不用拉了一兩頁纔在bindEvent裏面找到目標DOM。而且把updateOrder單獨作爲一個獨立的函數,其它地方若是須要也能夠使用,例如可能還有一個組合功能的操做可能會用到。另外把ajax再作一層抽象主要是這個東西實在是太經常使用,讓人一眼就知道要幹嗎,把它分離到另一個地方可讓具體的業務代碼更加簡單,例如上面發請求,我把回調函數準備好以後,只要執行一行代碼就行了。
你縮進太多層,一行就被空格佔掉了3、四十個字符,感觀上就不是很好,還會出現上面提到的,最後面要寫好多個右括號收尾的狀況,而且一個函數動不動就兩、三百行。
若是你使用了jQuery。
儘可能不要使用parent去獲取DOM元素,以下代碼:
var $activeRows = $this.parent().parent().children(".active");
這樣的代碼擴展性很差,一旦DOM結構發生改變,這裏的邏輯分分鐘會掛,如某天你可能會套了個div用來清除浮動,可是沒想到致使有個按鈕點不了了就坑爹了。
應該用closest,如:
var $activeRows = $this.closest(".order-list").find(".active");
直接定位和目標元素的最近共同祖先節點,而後find一下目標元素就行了,這樣就不會出現上面的問題,只要容器的類沒有變。若是你須要處理非本身的相鄰元素,能夠這麼搞:
$this.closest("li").siblings("li.active").removeClass("active"); $this.addClass("active");
有時候你能夠先把全部的li都置成某個類,而後再把本身改回去也是可取的,由於瀏覽器會進行優化,不會一見到DOM操做就馬上執行,會先排成一個隊列,而後再一塊兒處理,因此實際的DOM操做對本身先加一個類而後再去掉的正負相抵操做極可能是不會執行的。
以下代碼:
$(".page ul").addClass("shown"); $(".page .page-number").text(number); $(".page .page-next").removeClass("active");
上面的代碼作了三個全局查找,其實能夠優化一下:
var $page = $(".page"); $page.find("ul").addClass("shown"); $page.find(".page-number").text(number); $page.find(".page-next").removeClass("active");
先作一個全局查找,後續的查DOM都縮小到$page的範圍,$page的節點只有幾十個,在幾個裏面找就比在document幾百幾千個節點裏面查找要快多了。jQuery的查DOM也是用的querySelectorAll,這個函數除了用在document以外,可用在其它DOM結點。
有些人喜歡在綁事件以前先off掉,這樣感受能夠確保萬無一失,可是若是你綁的事件是匿名的,你極可能會把其它JS文件綁的一塊兒off掉了,而且這樣不容易暴露問題,有時候你的問題多是重複綁定事件,如點一次按鈕就綁一次就致使了綁屢次,因此根本緣由在這裏。你應該要確保事件只被綁一次,而不是確保每次寫以前都先off掉。若是你的事件容易出現綁屢次的狀況說明你的代碼組織有問題,這個在開發的時候應該是可以暴露出來的。
例如說一個表單只有幾個input元素,而後你給input加了個委託到form上面,甚至有時候是body上面,因爲事件冒泡致使在form上或者在頁面上的全部操做都會冒泡到form/body上,即便操做的不是目標元素,這樣jQuery就會收到在body上的事件,而後再判斷處理全部的操做的目標元素是否是你指定的那個,若是是再觸發你綁的回調函數。特別是像mousemove觸發得頻繁的事件都須要執行。因此若是元素比較少或者不須要動態增刪的那種就不要使用冒泡了,直接綁在對應的多個元素就行了。
例如獲取表單的input的和它的value:
let email = form.email.value.trim();
若是form裏面有一個input[name=email]的輸入框,就能夠這麼用。
再如,改變一個button的狀態,下面兩個其實差很少,可是若是獲取不到dom元素的話第一個會掛:
$("#update-order")[0].disabled = true; $("#update-order").prop("disabled", true);
設置一個元素的display爲block:
div.style.display = "block";
可是絕大多數的狀況下仍是要使用jq的API以確保兼容性,以下獲取scrollTop:
//在Firefox永遠返回0 let _scrollTop = document.body.scrollTop(); //正確方法 let scrollTop = $(window).scrollTop();
由於在firefox裏面須要使用:
document.documentElement.scrollTop
而這個在Chrome永遠返回0。再如window.innerWidth在某些低版本的安卓手機會有問題。因此當你不肯定兼容性的時候,就不要使用原生API,否則你得通過當心驗證後再使用。你能夠不用,但不是說不要去了解原生API,多去了解原生DOM操做仍是挺有幫助的。
以下代碼,頻繁地使用了window.location這個屬性:
let webLink = window.location.protocol + window.location.hostname; if(openType === "needtoTouch"){ webLink += "/admin/lead/list/page" + window.location.search.replace(/openType=needToTouch(&?)/, "") + window.location.hash; }
能夠先把它緩存一下,加快變量做用域查找:
let location = window.location; let webLink = location.protocol + location.hostname; if(openType === "needtoTouch"){ webLink += "/admin/lead/list/page" + location.search.replace(/openType=needToTouch(&?)/, "") + location.hash; }
當把location變成一個局部變量以後,它的查找時間將明顯快於全局變量。你可能會說就算再快這點時間對於用戶來講仍是沒有區別的吧,可是這是作爲一名程序員的追求,以及可讓代碼更簡潔。
以下代碼,若是是非選中狀態就把顏色置灰:
$menuItem.css("color", "#ccc");
反之顏色恢復正常:
$menuItem.css("color", "#000");
這樣的代碼有問題,若是之後顏色改了,那麼你須要改兩個地方,一個是CSS裏設置,另外一個是JS裏面設置,而JS寫的樣式特別容易被忽略,查起來也很差定位。好的作法應該是經過添加刪除類的方法:
//變成選中態 $menuItem.addClass("selected"); //變成非選中態 $menuItem.removeClass("selected");
而後再經過CSS給selected的類添加樣式。若是是button之類的控件能夠結合:disabled、:checked、:valid等僞類,連類都不用添加
可是有一種是必定要用JS控制的,就是須要先計算而後動態地改變position或者transform的值,若是用CSS3的transition實現不了.
添加非空判斷能夠提升代碼的穩健性,以下代碼:
//彈框時顯示other monthly charge showOtherMonthlyCharge: function(otherCharges, $dialog){ if(!otherCharges || !otherCharges.length){ return; } }
若是傳的爲空就不用處理,有時候你可能要拋個異常,告訴調用者。對一些比較重要的地方可能還要添加類型檢驗。後端傳的數據要確保會有那個屬性,若是不肯定也要添加非空判斷。若是調了第三方的API,添加出錯處理也很重要,由於你不能確保第三方API必定能正常工做,在一些你以爲可能會掛的地方作處理,如請求可能會超時,或者返回了undefined的異常結果,這種多使用通常可以發現。
以下代碼:
let a = [9, 3, 5]; for(let i in a){ console.log(a[i]) }
正常狀況下將會輸出數組的元素,可是很不幸的是,若是有人給數組原型添加了一個函數:
Array.prototype.add = function(){};
循環裏的i將會有4個值:0, 1, 2, "add",這樣就致使你的遍歷出現問題,因此數組遍歷應該使用length屬性或者數組的forEach/map方法。
JS裏面的表達式是能夠不用分號結尾,例如Zepto的源碼幾乎沒看到一個分號,可是咱們仍是提倡要每一個句子後面都要加上分號,這樣不容易出錯。
對於那些根據用戶輸入內容作跳轉,須要先把用戶內容作轉義,以下有問題的代碼:
let searchContent = form.search.value.trim(); window.location.href = `/search?key=${searchContent}`;
若是用戶輸入了一個#號如門牌號,將會致使#後面的內容看成錨點了,或者用戶可能會輸入一個空格。因此若是不肯定內容的東西須要先encode一下,以下代碼:
let searchContent = encodeURIComponent(form.search.value.trim()); window.location.href = `/search?key=${searchContent}`;
這樣跳轉就沒有問題了。
點擊一個容器的時候作跳轉,有些人喜歡這麼寫:
<div onclick="window.locatioin.href='/listing/detail?id={{listingId}}'"> <img> <div></div> </div>
其實這樣寫很差,不利於SEO,若是是一個跳轉應該用a標籤,以下:
<a href="window.locatioin.href='/listing/detail?id={{listingId}}'"> <img> <div></div> </a>
同時把a標籤變成塊級。就算你不用作SEO,也應當儘可能使用這種方式,由於用這種方式比較天然,還能夠控制是否要新開頁,若是在移動端也不用考慮click事件是否有延遲的問題。
因爲Safari的隱身模式下本地存儲會被禁用,若是你嘗試往localStorage寫數據的話,會報超出使用限制的錯誤:
QuotaExceededError (DOM Exception 22): The quota has been exceeded.
而Chrome的隱身窗口不會禁用。而使用Safari的用戶可能會開隱身窗口,特別是手機上的。這樣就致使代碼拋異常了,因此爲了兼容Safari,不能直接使用localStorage,要作個兼容:
Data.hasLocalStorage = true; try{ window.localStorage.trySetData = 1; }catch(e){ Data.hasLocalStorage = false; } setLocalData: function(key, value){ if(Data.hasLocalStorage){ window.localStorage[key] = value; } else{ util.setCookie("_LOCAL_DATA_" + key, value, 1000); } }, getLocalData: function(key){ if(Data.hasLocalStorage){ return window.localStorage[key]; } else{ return util.getCookie("_LOCAL_DATA_" + key); } }
上面代碼作了個兼容,若是不支持localStorage就使用cookie。要注意cookie一個域名最多隻能有4kB,50個key,而本地存儲限制爲5Mb.
let maxPrice = +form.maxPrice.value;
+號至關於Number:
let maxPrice = Number(form.maxPrice.value);
parseInt和Number有一個很大的區別是parseInt(「10px」)結果爲10,而Number(「10px」)是NaN,parseInt會更加天然,其它編程語言也有相似的轉換。可是Number仍是能適用不少的場景。
若是計算某個數字在第幾排:
let _row = Math.floor(index / columns); let row = parseInt(index / columns);
均可改爲:
let row = index / columns >> 0;
這個用位運算的效率會明顯高於上面兩個。
以下代碼:
let mobile = !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/)
有幾個值在if判斷裏面都返回false:0、false、」」、undefined、null、NaN都是false,因此判斷一個數組有沒有元素能夠這麼寫:
if (array.length) {}
而不用寫成:
if (array.length !== 0) {}
判斷一個字符串是否是空能夠寫成:
if (str) {}
可是判斷一個變量有沒有定義仍是要寫成:
if (typeof foo !== 「undefined」) {}
由於若是直接if變量的話,上面的幾個可能取值都將認爲是沒定義。
以下代碼,在發請求以前,常常須要獲取表單的值,而後去修改和添加老數據提交:
var orderData = { id: 123, price: 500 } orderData.price = 600; orderData.discount = 15; orderData.manageFee = 100;
其實有一種更優雅的方式那就是使用Object.assign:
var setOrderData = { price: 600, discount: 15, manageFee: 100 } Object.assgin(orderData, setOrderData);
使用這個的好處是能夠弄一個setOrderData的Object,寫成大括號的形式,而不用一個個去賦值,寫起來和看起來都比較累。最後再assign一下賦值給原先的Object就能夠了。
調試完就把console.log之類的打印信息去掉,別想着等一下作完了再刪,等一下就忘了。另外,不要使用alert調試,console/debugger上線了都沒事,通常用戶也不會開一個控制檯,可是alert上線了就完蛋了,特別是有些人喜歡用alert(「fuck」)之類的看下代碼有沒有運行到這裏,這種調試技巧仍是比較初級,要是真上線了可能得捲鋪蓋走人了。這也能夠經過代碼檢查工具作靜態檢查。
以下代碼:
let searchHandler = { search() { console.log(this); this.ajax(); }, ajax() { } }; $searchBtn.on("click", searchHandler.search);
當觸發searchBtn的點擊事件時,search函數裏的this已經指向 searchBtn了,由於它是click的回調函數:
searchHandler.search.call(btn, event);
因此函數運行環境就變成了btn了,所以這種單例的Object最好不要使用this,應直接使用當前命名空間的變量名:
let searchHandler = { search() { console.log(this); searchHandler.ajax(); }, ajax() { } }; $searchBtn.on("click", searchHandler.search);
這樣就沒問題了。
正則表達式能夠很方便地處理字符串,一般只要一行代碼就搞定了。例如去掉全局的某一個字符,如去掉電話號碼的-鏈接符:
phoneNumer = phoneNumber.replace(/\-/g, 「」);
或者反過來,把電話號碼改爲3-3-4的形式:
phoneNumber = phoneNumber.replace(/^(\d{3})(\d{3})(\d{4})$/, 「$1-$2-$3」);
熟練掌握正則表達式是每一個前端的基本技能。
當你一個函數要寫得很長的時候,例如兩、三百行,這個時候你考慮把這個大函數給拆了,拆成幾個小函數,而後讓主函數的邏輯變得清晰簡潔,而每一個小函數的功能單一獨立,使用者只須要管輸入輸出,而不須要關心內部是怎麼運行的。以下在地圖裏面處理用戶點擊的處理函數:
handleMouseClick(latLng, ajax = true) { var path = this.path; // 這裏調了一個closeToFirstPoint的函數判斷點擊位置是否接近第一個點 if(path.length >= 2 && ajax && this.closeToFirstPoint(latLng)){ // 若是是的話調closePath關閉路徑 this.closePath(ajax); return; } path.push({lat: latLng.lat(), lng: latLng.lng()}); // 調畫點的函數 this.drawPoint(latLng); // 調畫線的函數 this.drawSolidLine(); // 調畫多邊形背景的函數 this.drawPolygonMask(); this.lastMoveLine && this.lastMoveLine.setMap(null); this.$drawTip.hide(); }
上面拆成了不少個小函數,如畫點的drawPoint函數,使用這個函數只須要關心給它一個當前點的經緯度就能夠了,它就幫你畫一個點。
在函數之上又能夠繼續抽象,如把這個畫圖功能的模塊寫成一個DrawTool的類,這個類負責整個畫圖的功能,使用者只須要實例化一個對象,而後調一下init,傳一些參數就行了。
先抽成不一樣的函數,每一個函數負責一小塊,類似的函數彙集在一塊兒造成一個模塊,幾個模塊的相互調用又造成一個插件。
若是label裏面有input,監聽label的事件會觸發兩次,以下代碼:
<form id="choose-fruit"> <label> <input type="radio" name="fruit" value="apple"> <span>apple</span> </label> <label> <input type="radio" name="fruit" value="pear"> <span>pear</span> </label> <form> <script> { let $form = $("#choose-fruit"); $form.find("label").on("click", function(event){ console.log(event.target); }); } </script>
當點到span的時候,click事件會觸發兩次,若是label裏面沒有input的話,就只會觸發一次。這是爲何呢?由於在label容器內,點到span文字的時候會下發一次click事件給input,input事件又會冒泡到label,所以label會觸發兩次。所以若是你直接監聽label事件要注意注意觸發兩次的狀況
最近整理了一份HTML/CSS/JS編碼規範,供你們參考。
目錄:
1、HTML編碼規範
2、CSS編碼規範
3、JS編碼規範
根據W3C標準,img標籤要寫alt屬性,若是沒有就寫一個空的。可是通常要寫一個有內容的,根據圖片想要表達的意思,由於alt是在圖片沒法加載時顯示的文字。以下不太好的寫法:
<img src="company-logo.svg" alt="ABC Company Logo">
更好的寫法:
<img src="chime-logo.svg" alt="ABC Company">
這裏就不用告訴用戶它是一個Logo了,直接告訴它是ABC Compay就行了。再如:
<img src="user-head.jpg" alt="User Portrait">
可改爲:
<img src="user-head.jpg" alt="Bolynga Team">
若是圖片顯示不出來,就直接顯示用戶的名字。
有些人偷懶就直接寫個空的alt那也能夠,可是在一些重要的地方仍是要寫一下,畢竟它仍是有利於SEO.
爲何?由於寫了也沒用,還顯得你不懂html規範,咱們不是寫XHTML。常見的單標籤有img、link、input、hr、br,以下很差的寫法:
<img src="test.jpg"></img><br/> <input type="email" value=""/>
應改爲:
<img src="test.jpg"><br> <input type="email" value="">
若是你用React寫jsx模板,它就要求每一個標籤都要閉合,可是它始終不是原生html.
本身添加的非標準的屬性要以data-開頭,不然w3c validator會認爲是不規範的,以下很差的寫法:
<div count="5"></div>
應改爲:
<div data-count="5"></div>
以下很差的寫法:
<div> <li></li> <li></li> </div>
更常見的是td沒有寫在tr裏面:
<table> <td></td> <td></td> </table>
若是你寫得不規範,有些瀏覽器會幫你矯正,可是有些可能就沒有那麼幸運。由於標準並無說若是寫得不規範應該怎麼處理,各家瀏覽器可能有本身的處理方式。
有時候可能會直接在ul裏面寫一個div或者span,覺得這樣不要緊:
<ol> <span>123</span> <li>a</li> <li>b</li> </ol>
這樣寫也是不規範的,不能直接在ol裏面寫span,ol是一個列表,它的子元素應該都是display: list-item的,忽然冒出來個span,你讓瀏覽器如何自處。因此寫得不規範就會致使在不一樣的瀏覽器會有不一樣的表現。
一樣,tr的直接子元素都應該是td,你在td裏面寫tr那就亂了。
若是你用了section/aside/article/nav這種標籤的話,須要在裏面寫一個h1/h2/h3之類的標題標籤,由於這四個標籤能夠劃分章節,它們都是獨立的章節,須要有標題,若是UI裏面根本就沒有標題呢?那你能夠寫一個隱藏的標題標籤,若是出於SEO的目的,你不能直接display: none,而要用一些特殊的處理方式,以下套一個hidden-text的類:
<style>.hidden-text{position: absolute; left: -9999px; right: -9999px}</style> <section> <h1 class="hidden-text">Listing Detail</h1> </section>
使用section的好處是能夠劃分章節,以下代碼:
<body> <h1>Listing Detail</h1> <section> <h1>House Infomation</h1> <section> <h1>LOCATION</h1> <p></p> </section> <section> <h1>BUILDING</h1> <p></p> </section> </section> <section> <h1>Listing Picture</h1> </section> </body>
就會被outline成這樣的大綱:
Listing Detail
能夠使用html5 outliner進行實驗,能夠看到,咱們很任性地使用了多個h1標籤,這個在html4裏面是不合法的。
例以下面的寫法是不合法的:
<a href="/listing?id=123"> <div></div> </a>
a標籤是一個行內元素,行內元素裏面套了一個div的標籤,這樣可能會致使a標籤沒法正常點擊。再或者是span裏面套了div,這種狀況下須要把inline元素顯式地設置display爲block,以下代碼:
<a href="/listing?id=123" style="display: block"> <div></div> </a>
這樣就正常了。
設置頁面的渲染模式爲標準模式,若是忘記寫了會怎麼樣?忘記寫了會變成怪異模式,怪異模式下不少東西渲染會有所不一樣,怪異模式下input/textarea的默認盒模型會變成border-box,文檔高度會變成可視窗口的高度,獲取window的高度時就不是指望的文檔高度。還有一個區別,父容器行高在怪異模式下將不會影響子元素,以下代碼:
<div><img src="test.jpg" style="height:100px"></div>
在標準模式下div下方會留點空白,而在怪異模式下會。這個就提醒咱們在寫郵件模板時須要在頂部加上<!DOCType html>,由於在本地開發郵件模板時是寫html片斷,沒有這個的話就會變成怪異模式。
因爲郵件客戶端多種多樣,你不知道用戶是使用什麼看的郵件,有多是用的網頁郵箱,也有可能用的gmail/outlook/網易郵箱大師等客戶端。這些客戶端多種多樣,對html/css的支持也不一,因此咱們不能使用高級的佈局和排版,例如flex/float/absolute定位,使用較初級的table佈局可以達到兼容性最好的效果,而且還有伸縮的效果。
另外郵件模板裏面不能寫媒體查詢,不能寫script,不能寫外聯樣式,這些都會被郵件客戶端過濾掉,樣式都得用內聯style,你能夠先寫成外聯,而後再用一些工具幫你生成內聯html。
寫完後要實際測一下,能夠用QQ郵箱發送,它支持發送html格式文本,發完後在不一樣的客戶端打開看一下,看有沒有問題,如手機的客戶端,電腦的客戶端,以及瀏覽器。
因爲你不知道用戶是用手機打開仍是電腦打開,因此你不能把郵件內容的寬度寫死,可是徹底100%也很差,在PC大屏幕上看起來可能會太大,因此通常能夠這樣寫:
<table style="border-collapse:collapse;font-family: Helvetica Neue,Helvetica,Arial;font-size:14px;width:100%;height:100%"> <tr><td align="center" valign="top"><table style="border:1px solid #ececec;border-top:none; max-width:600px;border-collapse:collapse"> <tr><td>內容1</td></tr> <tr><td>內容2</td></tr> </table></td></tr></table>
最外面的table寬度100%,裏面的table有一個max-width:600px,相對於外面的table居中。這樣在PC上最大寬度就爲600px,而在手機客戶端上寬度就爲100%。
可是有些客戶端如比較老的outlook沒法識別max-width的屬性,致使在PC上太寬。可是這個沒有辦法,由於咱們不能直接把寬度寫死否則在手機上就要左右滑了,也不能寫script判斷ua之類的方法。因此沒法兼容較老版本outlook.
須要套不少層的,通常有兩種狀況,一種是切圖不太會,須要套不少層來維持排版,第二種是會切圖,可是把UI拆解得太細。像如下佈局:
我會這麼寫:
<ul> <li> <div class="img-container"> <img> </div> <div class="text"></div> </li> </ul>
由於它是一個列表,因此外面用ul/li做爲容器,裏面就兩個div,一個圖片的float: left,另一個文字的容器,這樣就能夠了。不用套不少層,可是有一些是必要的,你寫得太簡單的話,擴展性很差,例如它是一個列表那你就應該使用ul/ol,若是你要清除浮動,那你可能須要套一個clearfix的容器。
若是有一塊是總體,它總體向右排版,那麼這一塊也要套一個容器,有時候爲了實現一些效果,可能也要多套一個容器,例如讓外面的容器float,而裏面的容器display: table-cell。可是你套這些容器應該都是有價值,若是你只是爲了在功能上看起來整齊劃一,區分明顯好像沒太大的意義。
一般來講,在html裏面直接寫script和style是一種很差的實踐,這樣把樣式、結構和邏輯都摻雜在一塊兒了。可是有時候爲了不閃屏的問題,可能會直接在相應的html後面跟上調整的script,這種script看起來有點醜陋,可是很實用,是沒有辦法的辦法。
樣式不能寫在body裏,寫在body裏會致使渲染兩次,特別是寫得越靠後,可能會出現閃屏的狀況,例如上面的已經渲染好了,忽然遇到一個style標籤,致使它要從新渲染,這樣就閃了一下,無論是從碼農的追求仍是用戶的體驗,在body裏面寫style終究是一種下策。
一樣地script不要寫在head標籤裏面,會阻礙頁面加載。
而CSS也推薦寫成style標籤直接嵌在頁面上,由於若是搞個外鏈,瀏覽器須要先作域名解析,而後再創建鏈接,接着纔是下載,這一套下來可能已通過了0.5s/1s,甚至2~3秒。而寫在頁面的CSS雖然沒法緩存,可是自己它也不會很大,再加gzip壓縮,基本上在50k之內。
以下,若是是英文的網頁,應該這麼寫:
<html lang="en"> <html lang="en-US">
第一種表示它是英文的網頁,第二種表示它是美國英語的網頁,加上這個的好處是有利於SEO和屏幕閱讀器使用者,他能夠快速地知道這個網頁是什麼語言的,若是是中文能夠這麼寫:
<html lang="zh-CN">
以下,通常charset的meta標籤要寫在head標籤後的第一個標籤:
<head> <meta charset="utf-8"> </head>
一個緣由是避免網頁顯示unicode符號時亂碼,寫在前面是由於w3c有規定,語言編碼要在html文檔的前1024個字節。若是不寫的話在老的瀏覽器會有utf-7攻擊的隱患,具體能夠自行查閱資料,只是如今的瀏覽器基本都去掉了對utf-7編碼的支持了。
charset的標籤寫成html5的這種比較簡潔的寫法就好了,不須要寫成html4這種長長的:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
據我所查,就算是IE6也支持那種簡短的寫法,雖然它不是一個html5瀏覽器。
不要直接把Unicode的特殊符號直接拷到html文檔裏面,要使用它對應的實體Entity,經常使用的以下表所示:
符號 | 實體編碼 |
© | © |
¥ | ¥ |
® | ® |
> | > |
< | < |
& | & |
特別是像©這種符號,不要從UI裏面直接拷一個unicode的字符過去,若是直接拷過去會比較醜,它取的是用的字體裏面的符號。
有時候可能你須要在寫一個空的img標籤,而後在JS裏面動態地給它賦src,因此你可能會這麼寫:
<img src="" alt>
可是這樣寫會有問題,若是你寫了一個空的src,會致使瀏覽器認爲src就是當前頁面連接,而後會再一次請求當前頁面,就跟你寫一個a標籤的href爲空相似。若是是background-image也會有相似的問題。這個時候怎麼辦呢?若是你隨便寫一個不存在的url,瀏覽器會報404的錯誤。
我知道的有兩種解決方法,第一種是把src寫成about:blank,以下:
<img src="about:blank" alt>
這樣它會去加載一個空白頁面,這個沒有兼容問題,不會加載當前頁面,也不會報錯。
第二種辦法是寫一個1px的透明像素的base64,以下代碼所示:
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
第二種可能比較符合規範,可是第一種比較簡單,而且沒有兼容性問題。
有時候換行可能會引入空格,以下代碼:
<form> <label>Email: </label> <input type="email"> </form>
在label和input中間會有一個空格,這樣可能會致使設置lable的width和input 的width二者的和等於form的時候會致使input換行了,有時候你檢查半天沒查出緣由,最後可能發現,原來是多了一個空格,而這個空格是換行引發的。這個時候你可能會有一個問題,爲何<form>和<label>之間以及<input>和</form>之間的換行爲何沒引入空格?這是由於塊級元素開始的空白文本將會被忽略,以下Chrome源碼的說明:
// Whitespace at the start of a block just goes away. Don't even
// make a layout object for this text.
而且,塊級元素後面的空白文本結點將不會參與渲染,也就是說像這種:
<div></div> <div></div>
兩個div之間有textNode的文本節點,可是不會參與渲染。
要注意的是註釋標籤也是正常的頁面標籤,也會給它建立一個相應的節點,只是它不參與渲染。
以下使用-鏈接,不要使用駝峯式:
<div class="hello-world"></div>
是否能夠使用自定義標籤,像angular那樣都是用的自定義標籤,以下代碼:
<my-cotnainer></my-container>
通常不推薦使用自定義標籤,angular也有開關能夠控制是否要使用自定義標籤。雖然使用自定義標籤也是合法的,只要你給他display: block,它就像一個div同樣了,可是無論是從SEO仍是規範化的角度,自定義標籤仍是有點另類,雖然可能你會以爲它的語義化更好。
咱們知道,若是在頁面寫了兩個如出一轍的id,那麼查DOM的時候只會取第一個,同理重複的屬性也會只取第一個,以下:
<input class="books" type="text" name="books" class="valid">
第二個class將會被忽略,className重複了又會怎麼樣?重複的也是無效的,這裏主要是注意若是你直接操做原生className要注意避免className重複,以下代碼:
var input = form.books; input.className += " valid";
若是重複執行的話,className將會有重複的valid類。
例如,若是你要設置一個圖片的寬高,可能這麼寫:
<img src="test.jpg" alt width="400" height="300">
這個在ios的safari上面是不支持的,能夠自行實驗。
或者table也有一些能夠設置:
<table border="1"></table>
可是這種可以用CSS設置的就用CSS,可是有一個例外就是canvas的寬高須要寫在html上,以下代碼:
<canvas width="800" height="600"></canvas>
若是你用CSS設置的話它會變成拉伸,變得比較模糊。
標籤使用上不要太單調:
(1)若是內容是表格就使用table,table有自適應的優勢;若是是一個列表就使用ol/ul標籤,擴展性比較好
(2)若是是輸入框就使用input,而不是寫一個p標籤,而後設置contenteditable=true,由於這個在IOS Safari上光標定位容易出現問題。若是須要作特殊效果除外
(3)若是是粗體就使用b/strong,而不是本身設置font-weight
(4)若是是表單就使用form標籤,注意form裏面不能套form
(5)若是是跳鏈就使用a標籤,而不是本身寫onclick跳轉。a標籤裏面不能套a標籤
(6)使用html5語義化標籤,如導航使用nav,側邊欄使用aside,頂部和尾部使用header/footer,頁面比較獨立的部分能夠使用article,如用戶的評論。
(7)若是是按鈕就應該寫一個button或者<input type=」button」>,而不是寫一個a標籤設置樣式,由於使用button能夠設置disabled,而後使用CSS的:disabled,還有:active等僞類使用,例如在:active的時候設置按鈕被按下去的感受
(8)若是是標題就應該使用標題標籤h1/h2/h3,而不是本身寫一個<p class=」title」></p>,相反若是內容不是標題就不要使用標題標籤了
(9)在手機上使用select標籤,會有原生的下拉控件,手機上原生select的下拉效果體驗每每比較好,無論是IOS仍是android,而使用<input type=」tel」>在手機上會彈一個電話號碼的鍵盤,<input type=」number」> <input type=」email」>都會彈相應的鍵盤
(10)若是是分隔線就使用hr標籤,而不是本身寫一個border-bottom的樣式,使用hr容易進行檢查
(11)若是是換行文本就應該使用p標籤,而不是寫br,由於p標籤能夠用margin設置行間距,可是若是是長文本的話使用div,由於p標籤裏面不能有p標籤,特別是當數據是後端給的,可能會帶有p標籤,因此這時容器不能使用p標籤。
只要https的網頁請求了一張http的圖片,就會致使瀏覽器地址欄左邊的小鎖沒有了,通常不要寫死,寫成根據當前域名的協議去加載,用//開頭:
<img src=」//static.chimeroi.com/hello-world.jpg」>
文件名建議用小寫字母加中橫線的方式。爲何呢?由於這樣可讀性比較強,看起來比較清爽,像連接也是用這樣的方式,例如stackoverflow的url:
https://stackoverflow.com/questions/25704650/disable-blue-highlight-when-touch-press-object-with-cursorpointer
或者是github的地址:
https://github.com/wangjeaf/ckstyle-node
那爲何變量名不用小寫字母加小劃線的方式,如:family_tree,而是推薦用駝峯式的familyTree?C語言就喜歡用這種方式命名變量,可是因爲由於下劃線比較難敲(shift + -),因此通常用駝峯式命名變量的居多。
引入CSS文件的link能夠不用帶type="text/css",以下代碼:
<link rel="stylesheet" href="test.css">
由於link裏面最重要的是rel這個屬性,能夠不要type,可是不能沒有rel。
JS也是一樣道理,能夠不用type,以下代碼:
<script src="test.js"></script>
沒有兼容性問題。
屬性的書寫順序對於瀏覽器來講沒有區別,除了優先級覆蓋以外。可是若是順序保持一致的話,掃一眼能夠很快地知道這個選擇器有什麼類型的屬性影響了它,因此通常要把比較重要的屬性放前面。比較建議的順序是這樣的:
你可能會以爲我平時差很少就是這麼寫的,那麼說明你有一個比較好的素養。而且我以爲規則不是死,有時候能夠靈活,就像你可能會用transform寫居中,而後把left/top/transform挨在一塊兒寫了,我以爲這也是無可厚非的,由於這樣可讓人一眼看出你要幹嗎。
有些人可能喜歡用樣式的特色命名,例如:
.red-font{ color: red; } .p1{ font-size: 18px; } .p2{ font-size: 16px; }
而後你在它的html裏面就會看到套了大量的p1/p2/bold-font/right-wrap之類的類名,這種是不可取的,假設你搞了個red-font,下次UI要改顏色,那你寫的這個類名就沒用了,或者是在響應式裏面在右邊的排版在小屏的時候就會跑到下面去,那你取個right就沒用了。有些人先把UI總體瞄了一下,發現UI大概用了3種字號18px/16px/14px,因而寫3個類p1/p2/p3,不一樣的字號就套不一樣的類。這乍一看,好像寫得挺通用,可是當你看他的html時,你就瘋掉了,這些p1/p2/p3的類加起寫了有二三十個,密密麻麻的。我以爲若是要這樣寫的話還不如藉助標題標籤以下:
.house-info h2{ font-size: 18px; } .house-info h3{ font-size: 16px; }
由於把它的字號加大了,極可能是一個標題,因此爲何不直接用標題標籤,不能僅僅擔憂由於標題標籤會有默認樣式。
類的命名應當使用它所表示的邏輯意義,如signup-success-toast、request-demo、agent-portrait、 company-logo等等。
若是有些樣式你以爲真的特別通用,那能夠把它看成一個類,如clearfix,或者有些動畫效果,有幾個地方都要用到,我以爲這種較爲複雜而且通用的能夠單獨做爲一個類。可是仍是趨向於使用意義命名。
有些人在寫CSS的時候使用一些hack的方法註釋,以下:
.agent-name{ float: left; _margin: 10px; //padding: 20px; }
這種方法的原理是因爲//或者_開頭的CSS屬性瀏覽器不認識,因而就被忽略,分號是屬性終止符,從//到分號的內容都被瀏覽器忽略,可是這種註釋是不提倡的,要麼把.css文件改爲.less或者.scss文件,這樣就能夠愉快地用//註釋了。
還有一些專門針對特定瀏覽器的hack,如*開頭的屬性是對IE6的hack。無論怎麼樣都不要使用hack。
選擇器通常不要寫超過3個,有些人寫sass或者less喜歡套不少層,以下:
.listings-list{ ul{ li{ .bed-bath{ p{ color: #505050; } } } } }
一個容器就套一層,一層一層地套下來,最底層套了七八層,這麼長的選擇器的性能比較差,由於Chrome裏面是用遞歸從最後一個選擇器一直匹配到第一個,選擇器越多,匹配的時間就越長,因此時間會比較長,而且代碼的可讀性也比較差,爲看到最裏面的p標籤的樣式是哪一個的我得一層層地往上看,看是哪裏的p。代碼裏面縮進了七、8層看起來也比較累。
通常只要寫兩三個比較重要的選擇器就行了,不用每一個容器都寫進去,重要的目標元素套上class或者id。
最後一個選擇器的標籤的應該少用,由於若是你寫個.container div{}的話,那麼頁面上全部的div第一次都匹配中,由於它是從右往左匹配的,這樣的寫的好處是html不用套不少的類,可是擴展性很差,因此不要輕易這樣用,若是要用須要仔細考慮,若是合適才使用,最起碼不能濫用。
有時候會出現本身的樣式受到其餘人樣式的影響,或者本身的樣式不當心影響了別人,有多是由於類的命名和別人同樣,還有多是選擇器寫的範圍太廣,例若有人在他本身的頁面寫了:
* {
box-sizing: border-box; }
結果致使在他個頁面的公用彈框樣式掛了。一方面不要寫*全局匹配選擇器,無論從性能仍是影響範圍來講都太大了,例如在一個有3個子選擇器的選擇器:
.house-info .key-detail .location{}
在三個容器裏面,*都是適用的,而且有些屬性是會繼承的,像font-size,會致使這三個容器都有font-size,而後一層層地覆蓋。
還有一種狀況是濫用了:first-child、:nth-of-type這種選擇器,使用這種選擇器的後果是擴展性很差,只要html改了,就會致使樣式無論用了,或者影響到了其它無關元素。可是並非說這種選擇器就不能用,只要用得好仍是挺方便的,例如說在全部的li裏面要讓最後一個li的margin-left小一點,那麼能夠這麼寫:
.listing-list li:last-child{ margin-left: 10px; }
這可能比你直接套一個類強。可是無論怎麼樣,不能濫用,合適的時候才使用,而不是僅僅爲了少寫類名。
覆蓋是一種經常使用的策略,也是一種不太優雅的方式,以下代碼,爲了讓每一個house中間的20px的間距,可是第一個house不要有間距:
.house{ margin-top: 20px; } .house:first-child{ margin-top: 0; }
其實能夠改爲這樣:
.house + .house{ margin-top: 20px; }
只有前面有.house的.house才能命中這個選擇器,因爲第一個.house前面沒有,因此命不中,這樣看起來代碼就簡潔多了。
還有這種狀況,以下代碼所示:
.request-demo input{ border: 1px solid #282828; } .request-demo input[type=submit]{ border: none; }
其實能夠藉助一個:not選擇器:
.request-demo input:not([type=sbumit]){ border: 1px solid #282828; }
這樣看起來代碼也優雅了不少。
有一種覆蓋是值得的,那就是響應式裏面小屏的樣式覆蓋大屏,以下:
.container{ width: 1080px; margin: 0 auto; } @media (min-width: 1024px){ .container{ width: auto; margin: 0 40px; } }
大屏的樣式也能夠寫成:
@media (min-width: 1025px){ .container{ width: 1080px; margin: 0 auto; } }
我一開始是就是這麼寫的,爲了遵循減小覆蓋原則,可是後來發現這種實踐很差,代碼容易亂,寫成覆蓋的好處在於能夠在瀏覽器明顯地看到,小屏的樣式是覆蓋了哪一個大屏的樣式,這個在大屏的時候又是怎麼樣的。
上面提到:not可讓代碼簡潔,還有其它一些很好用的。例如說只有兩個的時候一個佔比50%,而有3個的時候一個佔比33%,這個用CSS就能夠實現,以下:
.listing-list li{ width: 33%; } .listing-list li:first-child:nth-last-child(2), .listing-list li:first-child:nth-last-child(2) ~ li{ width: 50%; }
當li是第一個元素而且是倒數第二個元素的時候以及和它相鄰的li被第二組選擇器命中,它的寬度是50%,也就是隻有兩個li的時候才能知足這個條件。
另外還能夠借用:hover/:focus/:invalid/:disabled等僞類選擇器完成一些簡單的交互。
important用來覆蓋屬性,特別是在CSS裏面用來覆蓋style裏的屬性,可是important仍是少用爲妙。有時候你爲了偷懶直接寫個!important,覺得這個的優先級是最高的了,每每螳螂捕蟬,黃雀在後,極可能過不了多久又要再寫一個優先級更高的覆蓋掉,這樣就略顯尷尬了。因此能少用仍是少用。若是要覆蓋仍是先經過增長添加選擇器權重的方式。
"程序猿最煩兩件事,第一件事是別人要他給本身的代碼寫文檔,第二件呢?是別人的程序沒有留下文檔"。註釋也是一樣道理,當看到不少綠色的註釋代碼神經會比較放鬆,而當看到揉成一團尚未註釋的代碼是比較壓抑的。CSS的註釋可包括:
/* * @description整個列表頁樣式入口文件 * @author yincheng.li */
/*詳情頁貸款計算器*/
/*爲了去除輸入框和表單點擊時的灰色背景*/ input, form{ -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
有時候你看源碼的時候你會看到一些TODO的註釋:
/* TODO(littledan): Computed properties don't work yet in nosnap. Rephrase when they do. */
表示這些代碼還有待完善,或者有些缺陷須要之後修復。而這種TODO的註釋通常編輯器會把TODO高亮。
注意不要寫一些錯誤的誤導的註釋或者比較廢話的註釋,這種還不如不寫,以下:
/* 標題的字號要大一點 */ .listings h2{ font-size: 20px; }
無論是JS/CSS,縮進都調成4個空格,若是你用的sublime,在軟件的右下角有一個Tab Size,選擇Tab Size 4,而後再把最上面的Indent Using Spaces勾上,這樣,當你打一個tab鍵縮進的時候就會自動轉換成4個空格。若是你使用vim,新增或者編輯~/.vimrc文件新增一行:
:set tabstop=4
也會自動把縮進改爲4個空格,其它編輯器自行查找設置方法。由於\t在不一樣的編輯器上顯示長度不同,而改爲空格能夠在不一樣人的電腦上格式保持一致。
多個選擇器共用一個樣式集,每一個選擇器要各佔一行,以下:
.landing-pop, .samll-pop-outer, .signup-success{ display: none; }
每一個屬性名字冒號後面要帶個空格,~、>、+選擇器的先後也要帶一個空格:
.listings > li{ float: left; }
例如:
.list{ border: 1px solid 0px; margin: 0px; }
應改爲:
.list{ border: 1px solid 0; margin: 0; }
可是有個特例,就是和時間有關的時間單位都要帶上秒s,以下兩個都是不合法的:
transition-duration: 0; transition: transform 0 linear;
以下:
color: rgb(80, 80, 80);
應改爲:
color: #505050;
由於使用rgb是一個函數,它還要計算一下轉換。若是是帶有透明度的再用rgba.
若是色值的六個數字同樣,那麼寫3個就好:
color: #ccc;
以下兩個意思同樣:
border: 0; border-width: 0;
而下面這兩個同樣:
border: none; border-style: none;
因此用0和none均可以去掉邊框。
你可能會說打包工具其實最後會幫我處理,但本身要保持一個良好的書寫習慣仍是很重要的。
注意使用系統字體的對應的font-family名稱,如SFUIText Font這個字體,在Safari是-apple-system,而在Chrome是BlinkMacSystemFont,因此font-family能夠這麼寫:
font-family{ font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
再如微軟雅黑,不少中文網站都用這個字體,要寫成:
font-family{ font-family: Microsoft YaHei; }
另外font-family不能在代碼任意設置,若是使用了自定義字體。以下代碼:
.title{ font-family: Lato Bold; }
由於若是你在代碼裏面寫了好多個font-family,到時候要總體替換網頁的字體就很麻煩了,正確的作法應該是這樣的:
h1, strong, b{ font-family: Lato Bold; font-weight: normal; }
若是須要加粗就用標題標籤,或者b/strong標籤,而且要把font-weight調回來,由於那個字體自己就有加粗效果了,若是font-weight再是粗體的話瀏覽器會用本身的算法繼續加粗。若是是細體怎麼辦,一方面通常細體用得比較少,另外一方面沒有細體的標籤,能夠經過套類的方式。
有些人喜歡設置z-index很大:
z-index: 99999;
覺得他是老大了,不會有人再比他高了,可是螳螂捕蟬,黃雀在後,很快得再寫一個:
z-index: 999999;
一般本身頁面的業務邏輯的z-index應該保持在個位數就行了。
通常的說法是說爲了提升性能,屬性要合併,但其實Chrome每一個屬性都是單獨的,就算你合在一塊兒,它也會幫你拆出來,如把margin拆成left/right/top/bottom,可是咱們仍是推薦寫成合的,由於它可讓代碼看起來更簡潔,代碼量更少,以下代碼:
.container{ margin-top: 20px; margin-left: 10px; margin-right: 10px; }
能夠寫成:
.container{ margin: 20px 10px 0; }
可是合在一塊兒寫了,要注意別覆蓋了其它的設置,如上面把margin-bottom設置成了0.
再如:
.banner{ background-image: url(/test.jpg); background-position: 0 0; background-repeat: no-repeat; }
能夠改爲:
.banner{ background: url(test.jpg) 0 0 no-repeat; }
以下代碼:
a.btn { float: left; display: block; width: 100px; height: 30px; }
第二行的display: block實際上是沒用的,由於若是你浮動了,目標元素就會具備塊級盒模型的特性,即便你display: table-cell或者inline也無論用。若是你是display: flex,那麼float將會被忽略。
一樣地,absolute定位和fixed定位也有一樣的效果,會把行內元素變成塊級的。
清除浮動有多種方法,通常用clearfix大法,雖然這個方法有缺陷,可是它比較簡單且可以適用絕大多數的場景,一個兼容IE8及以上的clearfix的寫法:
.clearfix:after{ content: ""; display: table; clear: both; }
就不要在末尾添加一個多餘元素的方法清除浮動了,雖然也可行,可是比較low.
通常來講font-family不須要添加引號,即便字體名稱帶有空格也不要緊,可是有一種狀況是必定要加上引號的,就是字體名稱恰好是關鍵詞,以下字體都須要加關鍵詞:
font-family: "inherit", "serif", "sans-serif", "monospace", "fantasy", and "cursive"
background-url: url("//cdn.test.me/test.jpg");
你不加也能夠,可是有一種必定要加,那就是url裏面帶有特殊字符沒有轉義,以下:
background-url: url(//cdn.test.me/hello world.jpg)
上面瀏覽器會去加載//cdn.test.me/hello,而後報404。這種狀況一般是圖片是用戶上傳的,圖片的名字帶有空格,後端給的url沒有對特殊字符作處理,就會有問題,因此當url是可變的時候,最好仍是帶上引號:
background-url: url('//cdn.test.me/hello world.jpg');
這樣瀏覽器就能正常加載圖片了。這種狀況最好的仍是從源頭上避免,但咱們也能夠作個兼容。
這兩個都是合法的,只是統一一下比較好,不能一會兒單引號,一會兒雙引號的,比較廣泛的推薦是html使用雙引號,css使用單引號。
(1)不要使用all屬性作動畫
使用transition作動畫的時候不要使用all全部屬性,在有一些瀏覽器上面可能會有一些問題,以下:
transition: all 2s linear;
在Safari上面可能會有一些奇怪的抖動,正確的作法是要用哪一個屬性作動畫就寫哪一個,若是有多個就用隔開,以下代碼所示:
transition: transform 2s linear, opacity 2s linear;
若是能用transform作動畫的,就不會使用left/top/margin等,由於transform不會形成重繪,性能要比position那些高不少,特別是在移動端的時候效果比較明顯。基本上位移的動畫都能用transform完成,不須要使用CSS2的屬性,如一個框從右到左彈出。
例如把一個框,從下到上彈出,能夠用jQuery的slideUp函數,或者本身寫setInterval函數處理,可是這些沒有比用CSS來得好。使用CSS,初始狀態能夠把框translate移動屏幕外,而後點擊的時候加上一個類,這個類的transform值爲0,而後再用transition作動畫就行了。
英文的單詞或者數字若是當前行排不下會自動切到下一行,這樣就致使每行長短不一,有時候可能不太美觀,可是不能使用word-break: break-all把一個單詞拆成兩行,還有一種是使用:
hyphens: auto;
它會把單詞拆成用-鏈接的形式,看起來好像挺合理,可是因爲它斷詞斷得不夠完全,有些單詞斷不了,長短不一的現象看起來也比較明顯,有些單詞還被拆成了兩行,因此還不如不加。
所以,不要使用斷詞。
這個和上面提到的font-family設置是同樣的,不要在代碼裏面手動設置font-family,以下:
.icon-up:before{ content: "\e950"; font-family: "icon-font"; }
正確的作法是給.icon-up的元素再套一個.icon的類,font-family等對圖標字體的相關設置都統一在這個類裏面:
.icon{ font-family: "icon-font"; /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
由於咱們可能會添加其它一些設置,有個.icon的類統一處理比較好。就不要手動一個個去設置font-family了。
因爲每一個瀏覽器都有本身的UA樣式,而且這些樣式還不太統一,因此須要作樣式reset,常見的reset有如下:
/* IE瀏覽器對輸入控件有本身的font-family,須要統一 */ input, textarea, button{ font-family: inherit; } /* Chrome瀏覽器會在輸入控制彙集的時候添加一個藍色的outline*/ input:focus, textarea:focus, select:focus{ outline: none; } /* 去掉textarea的可拉大小功能*/ textarea{ resize: none; } /* IOS Safari在橫屏的時候會放大字體,第二個屬性讓滑動更流暢 */ html{ -webkit-text-size-adjust: 100%; -webkit-overflow-scrolling : touch; } /* 統一標籤的margin值和p標籤的line-height*/ body, p, h1, h2, ul, ol, figure, li{ padding: 0; margin: 0; } h1, h2, p{ line-height: 150%; } /* 去掉select的默認樣式 */ select{ -webkit-appearance: none; } /* 若是有輸入內容IE會給輸入框右邊加一個大大的X */ input::-ms-clear{ display: none; width: 0; height: 0; } /* 去掉number輸入框右邊點擊上下的小三角 */ input::-webkit-inner-spin-button{ -webkit-appearance: none; } input::-webki-outer-spin-button{ -webki-appearance: none; }
無論是UI直接給的圖片仍是本身從UI圖裏切出來的圖片,都須要把圖片壓縮一下,建議使用tinypng,它能夠在保持圖片質量減小較低的狀況下,把圖片壓得很厲害,比直接在PS裏面設置壓縮質量要強。若是是色彩比較豐富的圖片要使用jpg格式,不能使用png格式,png會大得多,若是是logo那種矢量圖片,直接使用svg格式便可。通常來講要把圖片控制在300k之內,特別是banner頭圖,圖片的大小也要控制住。
顯示一張圖片有兩種方式,能夠經過設置CSS的background-image,或者是使用img標籤,究竟何時用哪一種呢?
若是是頭圖等直接展現的圖片仍是要img標籤,若是是作爲背景圖就使用background,由於使用img能夠寫個alt屬性加強SEO,而背景圖那種自己不須要SEO。雖然background有一個一個background-position: center center很好,可是頭圖那種仍是使用img吧,本身去居中吧,否則作不了SEO。
響應式開發最很差不要雜合使用rem,文字字號要麼所有使用rem,要麼不要用,也不要使用transform: scale去縮小,由於被縮小的字號看起來會有點奇怪,別人都是14px,而你變成了13.231px,小了一點。響應式的原則通常是保持中間或者兩邊間距不變,而後縮小主體內容的寬度。
:before和:after能夠用來畫頁面的一些視覺上的輔助性元素,如三角形、短的分隔線、短豎線等,能夠減小頁面上沒有用的標籤。可是頁面上正常的文本等元素仍是不要用before/after畫了。
首先absolute定位的元素渲染性能會比較高,由於它獨立出來了,計算量會少,用得好仍是能夠的。可是若是你頁面的主要佈局是使用absolute的那確定是不可取的,由於absolute定位的可擴展性不好,你把每一個元素的位置都定死了就變不了了,能夠多用float,雖然float的性能相對較差,可是無論是實用性仍是兼容性都是挺好的。
有些人喜歡用inline-block,特別是剛開始學切圖的人,由於block會換行,而inline-block不會換行還具備盒模型,所以inline-block用得很順手,而float比較複雜,還要處理清除浮動之類的問題。以下佈局:
應該寫li,而後讓li float,若是你讓li display:inline-block也能夠達到目的。可是inline-block用得多了可能會有一些奇怪的問題,你一般要在一個inline-block的元素裏面套block的元素,inline-block是行內元素,而block是塊級元素,這二者終究有點差異。這種應該用float/flex會更天然,若是你float用順手了你會發現比inline-block好多了,而且更加專業。若是你沒怎麼用過flex ,那你能夠嘗試換一下使用flex,若是你沒怎麼用過float,能夠嘗試用一下。只有你的切圖方式多樣化了,你切起圖來才能比較靈活。
通常來講,UI給的圖片展現寬高是固定的,可是實際的圖片長寬是不固定,大部分圖片是長是比寬大,小部分圖片是寬比長大。因此須要居中裁剪展現,以下圖所示:
中間黑色的框是展現區域,圖片的短邊和窗器的邊同樣大,另外一邊按圖片的原始比例拉伸,而後居中顯示。這個得藉助JS,由於圖片未加載好以前,不知道是長邊比較大仍是寬比較大。以下代碼:
<div class="img-container"> <img src="test.jpg" alt onload="resizeImg(this, '400px', '300px'"> </div>
藉助一個resizeImg函數,在onload函數裏面作處理。而後居中用CSS:
.img-container{ position: relative; width: 400px; height: 300px; } .img-container img{ position: absolute; left: -9999px; right: -9999px; top: -9999px; bottom: -9999px; margin: auto; }
上面代碼用了一個margin: auto作居中。
移動端的的一些圖標如X,可能會設計得比較小,因此點起來會不太好點,所以要提升可點區域範圍,可經過增長padding,以下代碼:
.icon-close{ position: abosulte; right: 0; top: 0; padding: 20px; }
這樣區域就增長了一圈,點起來就容易多了。
若是設置input的line-height,以下代碼,你可能要作垂直居中:
.request-demo input{ height: 40px; line-height: 40px; }
設置了line-height爲一個很高的值,這樣會致使Safari瀏覽器的輸入光標|變得巨大,因此若是你要居中的話,使用padding吧。
由於IOS Safari切換輸入框的時候會頁面會彈閃得很厲害,由於你在切的時候它會先把鍵盤收起來,而後再彈出來,這個時間很短,給人感受頁面彈閃了一下,但若是把body禁止滑動了就不會有這個問題,這有兩個解決辦法,第一種是把body fixed住,第二種設置body overflow: hidden,相對來講第二種比較簡單一點。IOS10徹底不會閃,IOS9如下仍是會閃。
有時候UI裏面會有一些漸變的效果,沒法複製CSS出來,這個時候能夠用一個在線的工具,生成漸變的CSS:www.cssmatic.com/gradient-ge…,可是這個須要本身手動調一個和UI如出一轍的效果,或者能夠直接給UI調一個它理想的效果,它會生成兼容性很強的CSS:
background: #fff; background: -moz-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: -webkit-gradient(left top, right top, color-stop(0%, #fff), color-stop(43%, #d2d2d2), color-stop(58%, #d1d1d1), color-stop(100%, #fefefe)); background: -webkit-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: -o-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: -ms-linear-gradient(left, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); background: linear-gradient(to right, #fff 0%, #d2d2d2 43%, #d1d1d1 58%, #fefefe 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff', endColorstr='#fefefe', GradientType=1 );
以下有些人爲了把span撐開,設置span display: inline-block:
span.phone-numer{ display: inline-block; margin-left: 10px; }
其實行內元素可直接margin的左右,可以把它撐開,不須要設置inline-block:
span.phone-numer{ margin-left: 10px; }
另外須要注意的是img/input/textarea/button默認就是inline-block,也不用再設置。
《代碼大全》這本書裏面有一章是專門講變量命名的,這裏結合這本書的建議作說明。總地來講,變量名要準確完整地描述該變量所表述的事物,具體來講:
如如下好的變量名和很差的變量名:
很差的變量名 | 好的變量名 |
inp | input, priceInput |
day1, day2, param1 | today, tomorrow |
id | userId, orderId |
obj | orderData, houseInfos |
tId | removeMsgTimerId |
handler | submitHandler, searchHandler |
左邊的變量名都不太清楚,代碼的擴展性很差,一旦代碼須要加功能的話,就容易出現obj一、obj二、obj3這種很抽象的命名方式。因此一開始就要把變量的名字起得真實有意義,不要搞一些很短很通用的名字。
固然變量名取得太長也很差,如maximumNumberOfTeamMembers.
變量名應直指問題領域,來源於現實世界,而不是計算機世界,例如取了texareaData之類的名字,應該取一個和業務相關的名字,如leaveMsg.
如up/down、begin/end、opened/closed、visible/invisible、scource/target,對仗明確可讓人很清楚地知道兩個變量的意義和用途。
有些喜歡取temp和obj之類的變量,若是這種臨時變量在兩行代碼內就用完了,接下來的代碼就不會再用了,仍是能夠接受的,如交換數組的兩個元素。可是有些人取了個temp,接下來十幾行代碼都用到了這個temp,這個就讓人很困惑了。因此應該儘可能少用temp類的變量,以下代碼:
var temp = 10; var leftPosition = currentPosition + temp, topPosition = currentPosition - temp;
應改爲:
var adjustSpace = 10; var leftPosition = currentPosition + adjustSpace, topPosition = currentPosition - adjustSpace;
《代碼大全》這本書建議布爾變量不用以is/do之類的開頭,如:
var isMobile = true, isError = true, doUpdate = false;
可改爲:
var mobile = true, error = true, updated = false;
還有其它一些經常使用的名稱如done/found/successs/ok/available/complete等,結合具體的語境:
var ajaxDone = true, fileFound = false, resourceUpdated = true;
另外變量名不要使用否認的名詞,如notOk,notReady,由於否認的詞取反的時候就會比較奇怪,如if(!notOk). 要使用確定的布爾變量名。若是是參數的話可結合ES6的默認形參值。
(6)變量名使用正確的語法
不要使用中文拼音,如shijianchuo應改爲timestamp,若是是複數的話加s,或者加上List,如orderList、menuItems,而過去式的加上ed,如updated/found等,若是正在進行的加上ing,如calling.
以下聲明三個變量:
var registerForm, question, calculateResult;
以上絕對是合法JS語法,可是這三個變量的用途會讓人比較困惑,特別是中間第二個question,問題是什麼。可是當你把上面的變量賦一個初始值的時候:
var registerForm = null, question = "", calculateResult = 0;
就讓人豁然開朗了,原來question是一個問題的字符串,而result是一個數字,form是一個對象。這也有利於JS解釋器提早作一些優化處理,不用等到使用的時候才知道這些變量是什麼類型的。
以下代碼:
function calculatePrice(seatCount){ if (seatCount <= 0) { return ""; } else { return seatCount * 79; } }
這個代碼可能返回整型,也有可能返回字符串,就會讓人比較困惑,同時從代碼性能來講也是不高的,雖然它是合法的JS語法,一個函數的返回類型要統一。你可能會說我用上面的函數作爲輸入框顯示的值,若是是負數或者0,那麼輸入框就不要顯示任何東西,因此纔會返回空的字符串。即便是這樣的緣由也不建議這樣寫,從長遠來看這樣寫是不利的,你應該用其它的方法組織你的代碼。要養成強類型的代碼風格,這樣不容易出bug,擴展也容易。另外若是一個變量你把它當成數字使用,下面就不要再把它當成字符串使用了,由於這樣也容易讓人困惑。微軟的Typescript就是一種強類型的書寫語法,不少大型項目會使用typescript寫JS,有興趣的能夠繼續瞭解怎麼寫typescript.
undefined表示一個變量未定義,你定義了一個變量又說它未定義自己就很奇怪。這可能會形成的問題是使用上的歧義,由於咱們常用undefined來判斷變量有沒有定義:
if (typeof window.HTMLVideoElement === "undefined")
若是要賦值應該要賦空值,如對象賦值爲null,數字賦值爲0,字符串賦值爲空字符,那你可能會說0也是一個正常的數字,若是賦值爲0會致使我誤認爲它是一個正常的數據,那怎麼辦呢?若是你的數字都是非負數,那麼能夠把初始值置爲-1,實在不行就置成NaN.
函數的返回值也不要顯式地return undefined.
一個比較流行的空格和縮進排版以下代碼所示:
//逗號後面帶個空格,) {中間帶個空格 function getSeatDiscount(seatCount, currentPrice) { //雙目運算符左右兩邊都帶個空格 var originPrice = editOrder.getSeatsPrice(seatCount); return Math.round((originPrice - currentPrice) / originPrice * 100); }
一行太長要換行,如V8的源碼裏面一行最長是70個字符,超過就換行:
function ArrayUnshift(arg1) { // length == 1 //if判斷裏面進行了換行,而且if (中間帶個空格 if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) && !%object_is_sealed(array)) { SparseMove(array, 0, 0, len, num_arguments); } else { SimpleMove(array, 0, 0, len, num_arguments); } }
一行代碼太長了就換行是一種好的習慣,太長讓人看起來比較費勁。基本上一行不要超過100個字符,超過就要換行,無論是註釋仍是代碼。
==會帶上類型轉換,這和上面同樣的,咱們要用強類型的風格寫代碼,因此不要使用==,若是有類型轉換本身作類型轉換,不要讓別人去猜這裏面有類型轉換,使用==會有一些比較奇怪的結果:
null == undefined //true '' == '0' //false 0 == '' //true 0 == '0' //true ' \t\r\n ' == 0 //true new String("abc") == "abc" //true new Boolean(true) == true //true true == 1 //true
對一些比較重要的常量起一個名字,例以下面的代碼:
const ONE_DATE = 3600 * 24 * 1000; var tomorrow = today + ONE_DATE;
再以下面很差的寫法:
dialogHandler.showQuestionNaire("seller", "sell", 5, true);
上面四個常量會讓人看起來比較困惑,若是能夠的話給它們起個名字,若是以爲麻煩那就加上註釋。
一個緣由是在全局做用域下,變量的查找時間會更長,第二個緣由是污染全局做用域,有時候會形成一些意想不到的結果,以下:
var name = "hi boy"; console.log(window.name);
定義了一個變量,可是恰好不巧window.name是原本有這個屬性,這個屬性一般用來跨域傳遞數據。若是你設置了name這個變量,就把全局的window.name給覆蓋了。
ES6新增了let/const定義變量,使用let有一些好處,如:
let me = "go"; // Uncaught SyntaxError: Identifier 'me' has already been declared let me = "go";
使用babel loader打包的時候它會作靜態檢查:
Module build failed: Duplicate declaration "me"
for(let i = 0; i <= 4; i++) { tasks.push(function(){ console.log("i is " + i); }); }
使用let使得i在for循環裏面每次運行的做用域都是獨立的。而且for裏定義的變量在for循環外是不可見的。
babel在轉換的時候,會在for循環裏面套一個function,而後把i看成函數的參數:
var _loop = function _loop(_i) { tasks.push(function () { console.log("i is " + _i); }); }; for (var _i = 0; _i <= 4; _i++) { _loop(_i); }
因爲let能夠避免變量重複定義,就衝着這一點,就使得它頗有意義。因此推薦多用let定義變量。因此本規範下面的變量將使用let代替var.
而const適合於給常量起個名字,如上面提到的:
const ONE_DAY = 3600 * 24 * 1000; const adjustSpace = 10;
或者是定義其它一些不須要修改的變量,防止不當心被其它代碼修改了。
能夠寫一行就不要寫三行,以下:
let seatDiscount = 100; if(seat < 5) { seatDiscount = 90; } else if(seat < 10) { seatDiscount = 80; } else { seatDiscount = 70; }
能夠改爲三目運算符:
let seatDiscount = seat < 5 ? 90 : seat < 10 ? 80 : 70;
代碼從8行減小到了2行。
例如如下代碼:
setTimeout(function(){ window.location.reload(true); }, 2000);
可改爲:
setTimeout(() => window.location.reload(true), 2000);
代碼從3行變成了1行。
對於通常的頁面的數據量來講,加減乘除等計算不足以形成性能瓶頸。容易形成瓶頸的是DOM操做,特別是大批量的DOM操做,只要一次有幾百上千的級別就容易形成頁面卡頓。特別是不要在一個for循環裏不斷地修改DOM,以下代碼:
for(var i = 0; i < 1000; i++) { ul.appendChild(li); }
這種能夠先把li拼好了,再一次性append到ul裏面,以下代碼:
var fragment = document.createDocumentFragment(); for(var i = 0; i < 1000; i++) { fragment.appendChild(li); } ul.appendChild(fragment);
若是你用jq的話應該先把模板渲染好,而後再一次性append到dom裏面,而不是不斷地append到dom裏面。如今的瀏覽器通常也比較智能,它會作一些優化,可是咱們不能總是期望瀏覽器會優化。
可是仍是要注意數據量特別大的狀況,你可能要使用setTimeout的方式分段處理數據,甚至使用多線程。使用setTimeout能夠這樣:
function sliceWorks(data, finishedCallback) { if(!data.length) { finishedCallback(); } else { const PIECES = 100; process(data.splice(0, PIECES)); setTimeout(() => sliceWorks(data, finishedCallback), 100); } }
咱們使用一個遞歸,把數據分段處理,每段100個,當數據處理完再調完成回調函數。
這個和CSS規範相似:
/* * @file listing-detail.js * @description 房源詳情頁的JS主文件,處理輪播、房貸計算器、約看房等邏輯 * @author yincheng.li * @update (yincheng.li 2017/8/19) */
/* * 和搜索界面展現有關的處理邏輯 * @namespace */ var searchWinHandler = { /* * 初始化驅動函數 * * @param {bool} realTimeSearch 是否須要進行實時搜索 * @param {HTMLFormElement} form 搜索表單DOM元素 * */ init(realTimeSearch, HTMLFormElement){ } /* * 搜索條件展現點擊X按鈕的處理函數 * * @param {object} jquery的點擊事件event * @trigger 會觸發search按鈕的點擊事件,以觸發搜索 * @returns 無返回 * * TODO 這裏臨時使用了一個全局變量的flag,這種實現方式不太好 * 雖然比較方便 */ closeFilterSpan(event){ } };
上面的@auhor @return都是註釋標籤,其它經常使用的註釋標籤還有:
/* @class 表示一個類 @constructor 構造函數 @deprecated 被棄用 @global 全局的變量 @namespace 具備命名空間做用的object,如$.fn.remove,$.fn.append,$和fn就是一個namespace,而fn是$的子命名空間 @this 這裏的this指向哪裏 @throws 在這個函數裏面可能會拋出什麼異常 @version 當前版本 */
對一些比較重要的變量加註釋,標明它是什麼用途,以及對一些核心代碼邏輯加上註釋,或者比較複雜的業務邏輯,寫了5個case,每一個case分別表明什麼;爲了改某個bug而加入的代碼,說明下爲了解決什麼問題;還有某些易混的判斷,爲何if判斷條件寫了四個,爲何代碼到這個if判斷不經過就直接return了;一些常量的註釋,爲何會忽然冒出來100這個數字;改動了別人的代碼,爲何要改動;等等。如:
var requestData = { listingId: listingData.listingId, page: 1, //把200改爲5,點擊More的時候是從新刷新頁面的,也沒有其餘地方用到, //不必請求那麼多,嚴重影響性能 pageSize: 5//200 };
總之多寫註釋仍是好的,只要不是廢話:
//定義了一個number的變量 let number = 5;
或者是和邏輯不符合的錯誤註釋。
還有一種排版的註釋,右括號的對應關係:
} //function ajax } //switch(b) } //if(a) } //searchHandler
主要是爲了方便在後面加代碼,例如我要在switch(b)後面加代碼,當我看到這個註釋我就很清楚地知道須要在哪裏按回車。不過通常不推薦嵌套很深的代碼,或者寫得很長,一個函數幾百行。
有些人的代碼常常會套個七八層,以jq代碼爲例,以下:
var orderHandler = { bindEvent: function(){ $(".update-order").on("click", function(){ if(orderStatus === "active"){ ajax({ url: "/update-order", success: function(data){ for(let i = 0; i < data.orders.length; i++){ dom.append(); } } }); } else { ajax({ url: "/create-order", success: function(data){ } }); } }); } };
上面的代碼最深的一層縮進了八層,你可能會以爲這樣邏輯挺清晰的啊,可是這種寫法同時也有點麪條式。以上代碼若是讓我寫,我會這麼組織:
var orderHandler = { sendUpdateOrderReq: function(requestUrl, successCallback){ ajax({ url: requestUrl, success: successCallback; }); }, updateOrder: function(event){ let requestUrl = orderStatus === "active" ? "/update-order" : "create-order"; //更新訂單回調函數 let activeUpdateCallback = function(data){ for(var i = 0; i < data.orders.length; i++){ console.log(data.orders[i].id); } }; //建立訂單回調函數 let inactiveUpdateCallback = function(data){ }; let successCallback = { active: activeUpdateCallback, inactive: inactiveUpdateCallback }; //發請求處理訂單 searchHandler.sendUpdateOrderReq(requestUrl, successCallback[orderStatus]); }, bindEvent: function(){ $(".update-order").on("click", searchHandler.updateOrder); } };
首先把綁定的匿名函數改爲有名的函數,這樣有個好處,當你想要off掉的時候隨時可off掉,而後能夠減小一層縮進,接着把根據orderStatus不一樣的回調先用變量判斷好,而不是同時積壓到後面再一塊兒處理。再把發送請求的函數再單獨抽出來作爲一個函數,這樣能夠減小兩層縮進。上面最深的縮進爲4層,減小了一半。而且你會發現這樣寫代碼邏輯會更加清晰,我在bindEvent裏面掃一眼就能夠知道哪些DOM綁了哪些事件,而後我對如對哪一個DOM的事件感興趣再跳到相應的回調函數去看,而不用拉了一兩頁纔在bindEvent裏面找到目標DOM。而且把updateOrder單獨作爲一個獨立的函數,其它地方若是須要也能夠使用,例如可能還有一個組合功能的操做可能會用到。另外把ajax再作一層抽象主要是這個東西實在是太經常使用,讓人一眼就知道要幹嗎,把它分離到另一個地方可讓具體的業務代碼更加簡單,例如上面發請求,我把回調函數準備好以後,只要執行一行代碼就行了。
你縮進太多層,一行就被空格佔掉了3、四十個字符,感觀上就不是很好,還會出現上面提到的,最後面要寫好多個右括號收尾的狀況,而且一個函數動不動就兩、三百行。
若是你使用了jQuery。
儘可能不要使用parent去獲取DOM元素,以下代碼:
var $activeRows = $this.parent().parent().children(".active");
這樣的代碼擴展性很差,一旦DOM結構發生改變,這裏的邏輯分分鐘會掛,如某天你可能會套了個div用來清除浮動,可是沒想到致使有個按鈕點不了了就坑爹了。
應該用closest,如:
var $activeRows = $this.closest(".order-list").find(".active");
直接定位和目標元素的最近共同祖先節點,而後find一下目標元素就行了,這樣就不會出現上面的問題,只要容器的類沒有變。若是你須要處理非本身的相鄰元素,能夠這麼搞:
$this.closest("li").siblings("li.active").removeClass("active"); $this.addClass("active");
有時候你能夠先把全部的li都置成某個類,而後再把本身改回去也是可取的,由於瀏覽器會進行優化,不會一見到DOM操做就馬上執行,會先排成一個隊列,而後再一塊兒處理,因此實際的DOM操做對本身先加一個類而後再去掉的正負相抵操做極可能是不會執行的。
以下代碼:
$(".page ul").addClass("shown"); $(".page .page-number").text(number); $(".page .page-next").removeClass("active");
上面的代碼作了三個全局查找,其實能夠優化一下:
var $page = $(".page"); $page.find("ul").addClass("shown"); $page.find(".page-number").text(number); $page.find(".page-next").removeClass("active");
先作一個全局查找,後續的查DOM都縮小到$page的範圍,$page的節點只有幾十個,在幾個裏面找就比在document幾百幾千個節點裏面查找要快多了。jQuery的查DOM也是用的querySelectorAll,這個函數除了用在document以外,可用在其它DOM結點。
有些人喜歡在綁事件以前先off掉,這樣感受能夠確保萬無一失,可是若是你綁的事件是匿名的,你極可能會把其它JS文件綁的一塊兒off掉了,而且這樣不容易暴露問題,有時候你的問題多是重複綁定事件,如點一次按鈕就綁一次就致使了綁屢次,因此根本緣由在這裏。你應該要確保事件只被綁一次,而不是確保每次寫以前都先off掉。若是你的事件容易出現綁屢次的狀況說明你的代碼組織有問題,這個在開發的時候應該是可以暴露出來的。
例如說一個表單只有幾個input元素,而後你給input加了個委託到form上面,甚至有時候是body上面,因爲事件冒泡致使在form上或者在頁面上的全部操做都會冒泡到form/body上,即便操做的不是目標元素,這樣jQuery就會收到在body上的事件,而後再判斷處理全部的操做的目標元素是否是你指定的那個,若是是再觸發你綁的回調函數。特別是像mousemove觸發得頻繁的事件都須要執行。因此若是元素比較少或者不須要動態增刪的那種就不要使用冒泡了,直接綁在對應的多個元素就行了。
例如獲取表單的input的和它的value:
let email = form.email.value.trim();
若是form裏面有一個input[name=email]的輸入框,就能夠這麼用。
再如,改變一個button的狀態,下面兩個其實差很少,可是若是獲取不到dom元素的話第一個會掛:
$("#update-order")[0].disabled = true; $("#update-order").prop("disabled", true);
設置一個元素的display爲block:
div.style.display = "block";
可是絕大多數的狀況下仍是要使用jq的API以確保兼容性,以下獲取scrollTop:
//在Firefox永遠返回0 let _scrollTop = document.body.scrollTop(); //正確方法 let scrollTop = $(window).scrollTop();
由於在firefox裏面須要使用:
document.documentElement.scrollTop
而這個在Chrome永遠返回0。再如window.innerWidth在某些低版本的安卓手機會有問題。因此當你不肯定兼容性的時候,就不要使用原生API,否則你得通過當心驗證後再使用。你能夠不用,但不是說不要去了解原生API,多去了解原生DOM操做仍是挺有幫助的。
以下代碼,頻繁地使用了window.location這個屬性:
let webLink = window.location.protocol + window.location.hostname; if(openType === "needtoTouch"){ webLink += "/admin/lead/list/page" + window.location.search.replace(/openType=needToTouch(&?)/, "") + window.location.hash; }
能夠先把它緩存一下,加快變量做用域查找:
let location = window.location; let webLink = location.protocol + location.hostname; if(openType === "needtoTouch"){ webLink += "/admin/lead/list/page" + location.search.replace(/openType=needToTouch(&?)/, "") + location.hash; }
當把location變成一個局部變量以後,它的查找時間將明顯快於全局變量。你可能會說就算再快這點時間對於用戶來講仍是沒有區別的吧,可是這是作爲一名程序員的追求,以及可讓代碼更簡潔。
以下代碼,若是是非選中狀態就把顏色置灰:
$menuItem.css("color", "#ccc");
反之顏色恢復正常:
$menuItem.css("color", "#000");
這樣的代碼有問題,若是之後顏色改了,那麼你須要改兩個地方,一個是CSS裏設置,另外一個是JS裏面設置,而JS寫的樣式特別容易被忽略,查起來也很差定位。好的作法應該是經過添加刪除類的方法:
//變成選中態 $menuItem.addClass("selected"); //變成非選中態 $menuItem.removeClass("selected");
而後再經過CSS給selected的類添加樣式。若是是button之類的控件能夠結合:disabled、:checked、:valid等僞類,連類都不用添加
可是有一種是必定要用JS控制的,就是須要先計算而後動態地改變position或者transform的值,若是用CSS3的transition實現不了.
添加非空判斷能夠提升代碼的穩健性,以下代碼:
//彈框時顯示other monthly charge showOtherMonthlyCharge: function(otherCharges, $dialog){ if(!otherCharges || !otherCharges.length){ return; } }
若是傳的爲空就不用處理,有時候你可能要拋個異常,告訴調用者。對一些比較重要的地方可能還要添加類型檢驗。後端傳的數據要確保會有那個屬性,若是不肯定也要添加非空判斷。若是調了第三方的API,添加出錯處理也很重要,由於你不能確保第三方API必定能正常工做,在一些你以爲可能會掛的地方作處理,如請求可能會超時,或者返回了undefined的異常結果,這種多使用通常可以發現。
以下代碼:
let a = [9, 3, 5]; for(let i in a){ console.log(a[i]) }
正常狀況下將會輸出數組的元素,可是很不幸的是,若是有人給數組原型添加了一個函數:
Array.prototype.add = function(){};
循環裏的i將會有4個值:0, 1, 2, "add",這樣就致使你的遍歷出現問題,因此數組遍歷應該使用length屬性或者數組的forEach/map方法。
JS裏面的表達式是能夠不用分號結尾,例如Zepto的源碼幾乎沒看到一個分號,可是咱們仍是提倡要每一個句子後面都要加上分號,這樣不容易出錯。
對於那些根據用戶輸入內容作跳轉,須要先把用戶內容作轉義,以下有問題的代碼:
let searchContent = form.search.value.trim(); window.location.href = `/search?key=${searchContent}`;
若是用戶輸入了一個#號如門牌號,將會致使#後面的內容看成錨點了,或者用戶可能會輸入一個空格。因此若是不肯定內容的東西須要先encode一下,以下代碼:
let searchContent = encodeURIComponent(form.search.value.trim()); window.location.href = `/search?key=${searchContent}`;
這樣跳轉就沒有問題了。
點擊一個容器的時候作跳轉,有些人喜歡這麼寫:
<div onclick="window.locatioin.href='/listing/detail?id={{listingId}}'"> <img> <div></div> </div>
其實這樣寫很差,不利於SEO,若是是一個跳轉應該用a標籤,以下:
<a href="window.locatioin.href='/listing/detail?id={{listingId}}'"> <img> <div></div> </a>
同時把a標籤變成塊級。就算你不用作SEO,也應當儘可能使用這種方式,由於用這種方式比較天然,還能夠控制是否要新開頁,若是在移動端也不用考慮click事件是否有延遲的問題。
因爲Safari的隱身模式下本地存儲會被禁用,若是你嘗試往localStorage寫數據的話,會報超出使用限制的錯誤:
QuotaExceededError (DOM Exception 22): The quota has been exceeded.
而Chrome的隱身窗口不會禁用。而使用Safari的用戶可能會開隱身窗口,特別是手機上的。這樣就致使代碼拋異常了,因此爲了兼容Safari,不能直接使用localStorage,要作個兼容:
Data.hasLocalStorage = true; try{ window.localStorage.trySetData = 1; }catch(e){ Data.hasLocalStorage = false; } setLocalData: function(key, value){ if(Data.hasLocalStorage){ window.localStorage[key] = value; } else{ util.setCookie("_LOCAL_DATA_" + key, value, 1000); } }, getLocalData: function(key){ if(Data.hasLocalStorage){ return window.localStorage[key]; } else{ return util.getCookie("_LOCAL_DATA_" + key); } }
上面代碼作了個兼容,若是不支持localStorage就使用cookie。要注意cookie一個域名最多隻能有4kB,50個key,而本地存儲限制爲5Mb.
let maxPrice = +form.maxPrice.value;
+號至關於Number:
let maxPrice = Number(form.maxPrice.value);
parseInt和Number有一個很大的區別是parseInt(「10px」)結果爲10,而Number(「10px」)是NaN,parseInt會更加天然,其它編程語言也有相似的轉換。可是Number仍是能適用不少的場景。
若是計算某個數字在第幾排:
let _row = Math.floor(index / columns); let row = parseInt(index / columns);
均可改爲:
let row = index / columns >> 0;
這個用位運算的效率會明顯高於上面兩個。
以下代碼:
let mobile = !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/)
有幾個值在if判斷裏面都返回false:0、false、」」、undefined、null、NaN都是false,因此判斷一個數組有沒有元素能夠這麼寫:
if (array.length) {}
而不用寫成:
if (array.length !== 0) {}
判斷一個字符串是否是空能夠寫成:
if (str) {}
可是判斷一個變量有沒有定義仍是要寫成:
if (typeof foo !== 「undefined」) {}
由於若是直接if變量的話,上面的幾個可能取值都將認爲是沒定義。
以下代碼,在發請求以前,常常須要獲取表單的值,而後去修改和添加老數據提交:
var orderData = { id: 123, price: 500 } orderData.price = 600; orderData.discount = 15; orderData.manageFee = 100;
其實有一種更優雅的方式那就是使用Object.assign:
var setOrderData = { price: 600, discount: 15, manageFee: 100 } Object.assgin(orderData, setOrderData);
使用這個的好處是能夠弄一個setOrderData的Object,寫成大括號的形式,而不用一個個去賦值,寫起來和看起來都比較累。最後再assign一下賦值給原先的Object就能夠了。
調試完就把console.log之類的打印信息去掉,別想着等一下作完了再刪,等一下就忘了。另外,不要使用alert調試,console/debugger上線了都沒事,通常用戶也不會開一個控制檯,可是alert上線了就完蛋了,特別是有些人喜歡用alert(「fuck」)之類的看下代碼有沒有運行到這裏,這種調試技巧仍是比較初級,要是真上線了可能得捲鋪蓋走人了。這也能夠經過代碼檢查工具作靜態檢查。
以下代碼:
let searchHandler = { search() { console.log(this); this.ajax(); }, ajax() { } }; $searchBtn.on("click", searchHandler.search);
當觸發searchBtn的點擊事件時,search函數裏的this已經指向 searchBtn了,由於它是click的回調函數:
searchHandler.search.call(btn, event);
因此函數運行環境就變成了btn了,所以這種單例的Object最好不要使用this,應直接使用當前命名空間的變量名:
let searchHandler = { search() { console.log(this); searchHandler.ajax(); }, ajax() { } }; $searchBtn.on("click", searchHandler.search);
這樣就沒問題了。
正則表達式能夠很方便地處理字符串,一般只要一行代碼就搞定了。例如去掉全局的某一個字符,如去掉電話號碼的-鏈接符:
phoneNumer = phoneNumber.replace(/\-/g, 「」);
或者反過來,把電話號碼改爲3-3-4的形式:
phoneNumber = phoneNumber.replace(/^(\d{3})(\d{3})(\d{4})$/, 「$1-$2-$3」);
熟練掌握正則表達式是每一個前端的基本技能。
當你一個函數要寫得很長的時候,例如兩、三百行,這個時候你考慮把這個大函數給拆了,拆成幾個小函數,而後讓主函數的邏輯變得清晰簡潔,而每一個小函數的功能單一獨立,使用者只須要管輸入輸出,而不須要關心內部是怎麼運行的。以下在地圖裏面處理用戶點擊的處理函數:
handleMouseClick(latLng, ajax = true) { var path = this.path; // 這裏調了一個closeToFirstPoint的函數判斷點擊位置是否接近第一個點 if(path.length >= 2 && ajax && this.closeToFirstPoint(latLng)){ // 若是是的話調closePath關閉路徑 this.closePath(ajax); return; } path.push({lat: latLng.lat(), lng: latLng.lng()}); // 調畫點的函數 this.drawPoint(latLng); // 調畫線的函數 this.drawSolidLine(); // 調畫多邊形背景的函數 this.drawPolygonMask(); this.lastMoveLine && this.lastMoveLine.setMap(null); this.$drawTip.hide(); }
上面拆成了不少個小函數,如畫點的drawPoint函數,使用這個函數只須要關心給它一個當前點的經緯度就能夠了,它就幫你畫一個點。
在函數之上又能夠繼續抽象,如把這個畫圖功能的模塊寫成一個DrawTool的類,這個類負責整個畫圖的功能,使用者只須要實例化一個對象,而後調一下init,傳一些參數就行了。
先抽成不一樣的函數,每一個函數負責一小塊,類似的函數彙集在一塊兒造成一個模塊,幾個模塊的相互調用又造成一個插件。
若是label裏面有input,監聽label的事件會觸發兩次,以下代碼:
<form id="choose-fruit"> <label> <input type="radio" name="fruit" value="apple"> <span>apple</span> </label> <label> <input type="radio" name="fruit" value="pear"> <span>pear</span> </label> <form> <script> { let $form = $("#choose-fruit"); $form.find("label").on("click", function(event){ console.log(event.target); }); } </script>
當點到span的時候,click事件會觸發兩次,若是label裏面沒有input的話,就只會觸發一次。這是爲何呢?由於在label容器內,點到span文字的時候會下發一次click事件給input,input事件又會冒泡到label,所以label會觸發兩次。所以若是你直接監聽label事件要注意注意觸發兩次的狀況