布流布局出來好多年了,剛出來時,一般都采用js來實現(xiàn),涌現(xiàn)了很多比較好的js插件,例如:masonry.js、gridify.js等等。這里就不再詳說如何使用了,網(wǎng)上都能找到。今天討論的是純CSS實現(xiàn)瀑布流布局。
這種布局方法非常簡單,就是將多個列表橫向排列,每個列表擁有的項都差不多。圖片寬度固定,高度自動撐起來。
html:
<div class="wrap"> <ul class="waterfall"> <li class="item"><img src="image/1.jpg"></li> <li class="item"><img src="image/3.jpg"></li> <li class="item"><img src="image/5.jpg"></li> <li class="item"><img src="image/7.jpg"></li> <li class="item"><img src="image/9.jpg"></li> </ul> <ul class="waterfall"> <li class="item"><img src="image/2.jpg"></li> <li class="item"><img src="image/4.jpg"></li> <li class="item"><img src="image/6.jpg"></li> <li class="item"><img src="image/8.jpg"></li> <li class="item"><img src="image/10.jpg"></li> </ul> </div>
css:
.wrap{ display:flex; justify-content: space-between; } .waterfall{ width:50%; } .waterfall .item img{ width:100%; }
注:
1、waterfall也可以浮動,wrap清除浮動,只要兩個列表在一行就行。
2、每一塊 item 都是從上往下排列的,并不能實現(xiàn)從左往右,解決辦法是:在獲取數(shù)據(jù)時,需要將數(shù)據(jù)按奇偶進(jìn)行分類顯示(假設(shè)顯示兩列,多列同理)。您可以定義兩個數(shù)組:listEven和listOdd,分別來接受獲取的數(shù)據(jù)的偶數(shù)項和奇數(shù)項,然后分別進(jìn)行渲染。這里用VUE來演示:
//假設(shè)res.data為ajax請求到的數(shù)據(jù) res.data.map(function(item,index){ index%2===0 ? that.listEven.push(item) : that.listOdd.push(item); })
<div class="wrap"> <ul class="waterfall"> <li class="item" v-for="(item,index) in listEven" :key="'even'+index"><img :src="item"></li> </ul> <ul class="waterfall"> <li class="item" v-for="(item,index) in listOdd" :key="'odd'+index"><img :src="item"></li> </ul> </div>
3、這種布局方法,會有一個問題,如果item高度左右列相差很大,就會顯示有問題,如下圖,解決辦法給item一個max-height,超過圖片這個max-height就隱藏,此外,后臺在傳圖片時做裁切更好:
css 瀑布流
CSS3 column多列可以非常輕松的實現(xiàn)瀑布流效果,它會自動將列表項分配到根據(jù)設(shè)置的列數(shù)當(dāng)中去,先看下它的常用屬性:
html
<ul class="waterfall"> <li class="item"><img src="1.jpg"></li> <li class="item"><img src="2.jpg"></li> <li class="item"><img src="3.jpg"></li> <li class="item"><img src="4.jpg"></li> <li class="item"><img src="5.jpg"></li> <li class="item"><img src="6.jpg"></li> <li class="item"><img src="7.jpg"></li> <li class="item"><img src="8.jpg"></li> <li class="item"><img src="9.jpg"></li> <li class="item"><img src="10.jpg"></li> </ul>
css
.waterfall{ column-count:2; } .waterfall .item{ break-inside: avoid; }
效果如下:
css3瀑布流布局
注:
1、每一塊 item 都是從上往下排列的,并不能實現(xiàn)從左往右,第一種方法采用了奇偶兩個數(shù)組解決了,這個不好解決,如果帶有上拉加載,不確定總個數(shù),就更不好解決了。
2、item的上下間距盡量不要用margin,否則可能會出現(xiàn)下面這種情況,這是因為左列最后一個margin跑到右邊上面去了,改成padding即可,因為padding屬于item的內(nèi)容,而break-inside: avoid則禁止item折斷:
css3瀑布流布局
CSS3的flex用法很多。大多數(shù)情況都是用來橫排,將塊狀容器顯示在一行,還有就是用來對其一行。但是,改變flex-direction就可以做到縱向排列。
html
<ul class="waterfall"> <li class="item"><img src="1.jpg"></li> <li class="item"><img src="2.jpg"></li> <li class="item"><img src="3.jpg"></li> <li class="item"><img src="4.jpg"></li> <li class="item"><img src="5.jpg"></li> <li class="item"><img src="6.jpg"></li> <li class="item"><img src="7.jpg"></li> <li class="item"><img src="8.jpg"></li> <li class="item"><img src="9.jpg"></li> <li class="item"><img src="10.jpg"></li> </ul>
css
.waterfall{ /*flex-box*/ display: flex; /*縱向排列*/ flex-direction: column; /*換列*/ flex-wrap: wrap; height: 100vh; } .waterfall .item{ width:50%; }
注:
1、flex-wrap: wrap 是為了讓item撐滿waterfall的時候換列(可以理解為換行)
2、這里waterfall的height一定要寫,否則,waterfall隨著內(nèi)容變多,不斷撐高也不會換列。
3、waterfall的height要隨著item的數(shù)量變化而變化,否則,一列顯示不了就顯示兩列,兩列顯示不了就顯示三列,為了維持固定的列數(shù),只能不斷升高waterfall的高度。
4、flex-direction: column和flex-wrap: wrap可以合并為flex-flow: column wrap;
5、與前面兩種方法一樣,這種方法仍然是不好解決從上往下排列而非從左往右排列的問題。
總結(jié):純CSS實現(xiàn)瀑布流,總有這樣或那樣的問題,最終還是要通過js來控制。如果對排序沒有要求的話,第二種方法還是比較好用的。純css實現(xiàn)瀑布流,大家還有什么好的方法,歡迎留言評論。關(guān)注IT學(xué)堂,會有更多驚喜哦!
我自己是一名從事了多年開發(fā)的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年我花了一個月整理了一份最適合2020年學(xué)習(xí)的web前端學(xué)習(xí)干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關(guān)注我的頭條號并在后臺私信我:前端,即可免費獲取。
使用demo
div { -ms-transform: rotate(0); -webkit-transform: rotate(0); -moz-transform: rotate(0); -o-transform: rotate(0); transform: rotate(0);}
CSS前綴自動補全:autoprefixer
已知: margin:20px; border:10px; padding:10px; width:200px; height:50px;

標(biāo)準(zhǔn)盒模型:1\. 占用寬:margin*2+padding*2+border*2+width=20*2+10*2+10*2+200=2802\. 占用高:margin*2+padding*2+border*2+height=20*2+10*2+10*2+50=1303\. 盒子實際寬度:padding*2+border*2+width=10*2+10*2+200=2404\. 盒子實際高度padding*2+border*2+height=10*2+10*2+50=90

怪異盒模型:1\. 占用寬:margin*2+width=20*2+200=2402\. 占用高:margin*2+height=20*2+50=903\. 盒子實際寬度:width=2004\. 盒子實際高度height=50
IE8及更早版本不兼容問題解決方案:在HTML頁面聲明
BFC(塊狀格式化上下文,獨立的盒子,布局不受外部影響,但是如果同一個BFC中,同級塊狀元素的margin-top和margin-bottom會重疊)
只要元素滿足下面的任一條件,都會觸發(fā)BFC特征。
解決問題:
元素被當(dāng)成行內(nèi)元素排版時,元素直接的空白符會被瀏覽器處理,根據(jù)white-spack的處理方式(默認(rèn)是normal,合并多余空白),Html代碼在回車換行時被轉(zhuǎn)成一個空白符,在字體不為0的情況下,空白符占據(jù)一定寬度,所以inline-block元素之間就出現(xiàn)了空隙。
復(fù)現(xiàn)<ul> <li>首頁</li> <li>登陸</li> <li>資源</li> <li>社區(qū)</li> <li>幫助</li></ul>

解決辦法:

優(yōu)先級相同時會發(fā)生什么? 樣式被覆蓋
normalize.css是一個css reset的替代方案。
ul>li:nth-child(2n+1) { background-color: red; }ul>li:nth-child(2n) { background-color: yellow;}
ul>li:first-child { border-top: none; }
有夢想的人,眼睛會發(fā)光。
希望屏幕前的你,能夠關(guān)注我一波。接下來,我會分享前端各種干貨,以及編程中的趣事。
天這篇文章我們開始詳細(xì)講解JS的事件。之前我們已經(jīng)講過許多事件,而我們今天要講的主要是事件對象。之前我們學(xué)習(xí)的onclick事件用于得知一個元素是否被點擊,但如果我們想知道一些更詳細(xì)的信息,例如點擊位置,點擊鍵是左鍵還是右鍵,就需要用到event對象。又例如我們希望檢測用戶鍵盤按的到底是左鍵還是右鍵來完成相應(yīng)的功能,也需要用到event對象。
event對象的作用是幫助我們獲取一些事件的信息,例如鼠標(biāo)位置,鍵盤按鍵。這里我們用一個小例子來說明event對象是怎么運作的。現(xiàn)在我們試圖實現(xiàn)一個效果:無論點擊頁面的什么地方,都可以彈窗。聰明的同學(xué)可能已經(jīng)猜到,我們可以直接給body添加onclick事件來實現(xiàn)這一點:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title>無標(biāo)題文檔</title> <script>window.onload=function (){ document.body.onclick=function () { alert('a'); };}; </script> </head> <body> </body> </html>
但實際上你會發(fā)現(xiàn),無論怎么點擊頁面都無法進(jìn)行彈窗,這是為什么呢?現(xiàn)在我們對這個頁面改造一下:
<html> <head> <meta charset="utf-8" /> <title>無標(biāo)題文檔</title> <script>window.onload=function (){ document.body.onclick=function () { alert('a'); };};</script> </head> <body style="border:1px solid black;"> <input type="button"value="aaa"> </body> </html>
可以看到的是,在body內(nèi)沒有內(nèi)容的時候,body頁面是撐不起來的(至少在ie下是這樣的),我們在頁面中加入了一個button后,body的大小也隨著button略微增大。當(dāng)然我們可以從樣式角度去解決這個問題,但是我們現(xiàn)在從程序的角度考慮的話,應(yīng)該怎么去解決他呢?其實很簡單,我們不選擇給body添加onclick事件而是直接給document添加就可以了:
window.onload=function (){ document.onclick=function () { alert('a'); }; };
效果如下:
這里body也沒有被撐起來,但是document代表的是整個網(wǎng)頁,因此可以通過點擊頁面任何地方彈窗。這里我們通過一個例子來講講document到底是什么東西。
window.onload=function (){ alert(document.childNodes[0].tagName); };
效果如下:
這里本應(yīng)該打印出document第一個子節(jié)點的標(biāo)簽名,但返回結(jié)果為undefined。順便大家可以用IE7測試一下,其返回結(jié)果會為一個感嘆號。聰明的同學(xué)應(yīng)該可以猜到,感嘆號其實就是DOCTYPE聲明中的第一個字符,DOCTYPE聲明本身也是作為一個節(jié)點存在的。
實際上我們可以這么理解:在一個頁面里,DOCTYPE和html存在同一個父級,也就是document(一個看不見的虛擬節(jié)點),他包括了整個網(wǎng)頁的內(nèi)容和網(wǎng)頁聲明。我們提了這么多關(guān)于document的知識,為的就是告訴大家一點:如果想給整個頁面添加事件,那么使用document一定比使用html好,因為html頁面是有可能撐不開的。回到我們的事件對象來。我們通過event對象可以獲取事件的信息,例如可以退獲取點擊的坐標(biāo):
window.onload=function (){ document.onclick=function (ev) { //IE //alert(event.clientX+','+event.clientY); //FF //alert(ev.clientX+','+ev.clientY); var oEvent=ev||event; alert(oEvent.clientX+','+oEvent.clientY); }; };
在ie瀏覽器下,直接通過event.clientX和event.clientY就可以獲取點擊的坐標(biāo),而在火狐下,event對象是不兼容的,需要給事件讀入一個參數(shù)(參數(shù)本身就是事件對象,由系統(tǒng)讀入),用這個參數(shù)來調(diào)用clientX和clientY屬性獲取坐標(biāo)。而“var oEvent=ev||event;”這種寫法,系統(tǒng)會自動獲取為真的一邊,這樣就可以通過一句話完美解決兼容性的問題了。
說完事件對象后我們來看看事件流?事件流說的簡單一點,其實就是事件跟水一樣從頭到尾流動的過程。我們來看一個關(guān)于事件流的最簡單的小例子:
<!DOCTYPE HTML> <html onclick="alert('html');"> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <style> div {padding:100px;} </style> </head> <body onclick="alert('body');"> <div style="background:#CCC;" onclick="alert(this.style.background);"> <div style="background:green;" onclick="alert(this.style.background);"> <div style="background:red;" onclick="alert(this.style.background);"> </div> </div> </div> </body> </html>
效果如下:
當(dāng)我們點擊最里面的一個div時,它會連續(xù)彈出多個框,原因大家應(yīng)該都明白。這里,就涉及到了事件冒泡的知識——在這個程序里,當(dāng)我們點擊了最里面的div并執(zhí)行事件后,還會將這個事件傳遞給父級繼續(xù)執(zhí)行,依次類推,直到html和document。這就是所謂的事件冒泡——事件會隨著層級依次傳遞到底。
事件冒泡學(xué)完以后大家更多的疑問肯定是,我們能用它來做點什么,畢竟學(xué)了任何知識都是拿來用的,而不是看的。一個小小的結(jié)論是:在我們平時做東西的時候,真正主動去利用時間冒泡去做的事情非常少,甚至可以說,很多時候事件冒泡都會給我們帶來煩惱腦。為什么這么說呢,下面我們來看一個小例子,立刻就能明白了。
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <style> #div1 {width:400px; height:300px; background:#CCC; display:none;} </style> <script> window.onload=function () { var oBtn=document.getElementById('btn1'); var oDiv=document.getElementById('div1'); oBtn.onclick=function (ev) { var oEvent=ev||event; oDiv.style.display='block'; //alert('按鈕被點擊了'); oEvent.cancelBubble=true; }; document.onclick=function () { oDiv.style.display='none'; //alert('document被點擊了'); }; }; </script> </head> <body> <input id="btn1" type="button" value="顯示" /> <div id="div1"> </div> </body> </html>
我們試想一下,我們想要實現(xiàn)一個功能:點擊頁面任意地方,都可以讓已經(jīng)顯示的div塊隱藏,根據(jù)我們前面所學(xué)的知識,最好的方法應(yīng)該是給document添加事件。但如果我們直接這么做的話,就會存在下面這個問題:我們在點擊按鈕的時候,這個事件也會被傳遞到其根元素document上,也就是說,點擊了按鈕后,會觸發(fā)兩個事件,div依然會被隱藏。取消冒泡的方式也是通過事件對象,在event對象里有一個屬性叫cancelBubble,當(dāng)我們將它設(shè)置為true的時候,就可以取消該元素的事件冒泡了。
下面我們來說一下鼠標(biāo)事件,之前說過了,clientX,clinentY可以獲取鼠標(biāo)的坐標(biāo)。不過,我們僅僅說它是鼠標(biāo)的坐標(biāo),而并沒有說它是鼠標(biāo)的什么坐標(biāo)。現(xiàn)在我們來看一下clientX,clinentY有一些小問題。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>無標(biāo)題文檔</title> <style> #div1 {width:200px; height:200px; background:red; position:absolute;} </style> <script> document.onmousemove=function (ev) { var oEvent=ev||event; var oDiv=document.getElementById('div1'); oDiv.style.left=oEvent.clientX+'px'; oDiv.style.top=oEvent.clientY+'px'; };</script> </head> <body> <div id="div1"> </div> </body> </html>
效果如下:
我們讓div1坐標(biāo)就等于鼠標(biāo)的當(dāng)前坐標(biāo),可以看到我們實現(xiàn)了一個div隨著鼠標(biāo)移動的效果。但這個程序存在一個小小的隱患——例如,現(xiàn)在如果我們把body的高度設(shè)置為2000px,會發(fā)生什么呢?實際上,無論是clientX還是clientY,它們代表的實際含義都是可視區(qū)坐標(biāo),而div則是根據(jù)body來定位的,所以當(dāng)可視區(qū)并不在頁面頂端時,必然會出現(xiàn)鼠標(biāo)和div之間的錯位。這里,我們可以使用scrollTop來解決這個問題。
<html> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <style> #div1 {width:200px; height:200px; background:red; position:absolute;} </style> <script> document.onmousemove=function (ev){ var oEvent=ev||event; var oDiv=document.getElementById('div1'); var scrollTop=document.documentElement.scrollTop||document.body.scrollTop; oDiv.style.left=oEvent.clientX+'px'; oDiv.style.top=oEvent.clientY+scrollTop+'px'; }; </script> </head> <body style="height:2000px;"> <div id="div1"></div> </body> </html>
這樣,無論是否滾動頁面,都不會出現(xiàn)錯位的問題了(不過會出現(xiàn)div抖動的情況,我們以后會有方法解決)。一個小小的經(jīng)驗是:但凡是你用到clientX,clientY的時候一定要加上scrollLeft或者scrollTop,否則很容易出問題。
現(xiàn)在我們來看一下其另一個例子(這里我們將scroll封裝成一個函數(shù)getPos。
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <style> div {width:10px; height:10px; background:red; position:absolute;} </style> <script> function getPos(ev){ var scrollTop=document.documentElement.scrollTop||document.body.scrollTop; var scrollLeft=document.documentElement.scrollLeft||document.body.scrollLeft; return {x: ev.clientX+scrollLeft, y: ev.clientY+scrollTop};}document.onmousemove=function (ev){ var aDiv=document.getElementsByTagName('div'); var oEvent=ev||event; var pos=getPos(oEvent); for(var i=aDiv.length-1;i>0;i--) { aDiv[i].style.left=aDiv[i-1].offsetLeft+'px'; aDiv[i].style.top=aDiv[i-1].offsetTop+'px'; } aDiv[0].style.left=pos.x+'px'; aDiv[0].style.top=pos.y+'px'; }; </script> </head> <body><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div> </body> </html>
效果如下:
這里制作出了一串div跟隨著鼠標(biāo)走的特效。我們讓后一個div都跟著前一個div走,第一個div跟著鼠標(biāo)走,就可以實現(xiàn)這種效果了。
說完鼠標(biāo)事件之后,我們來看看鍵盤事件。通過keyCode,我們可以獲取用戶操作的是哪個按鍵。這里我們需要介紹兩個新事件:onkeydown和onkeyup,分別代表按下鍵盤按鍵和放開鍵盤按鍵的事件。
這里通過一個小例子來說明他們是怎么用的:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <script> document.onkeydown=function (ev) { var oEvent=ev||event; alert(oEvent.keyCode); }; </script> </head> <body> </body> </html>
可以看到的是,我們輸入一個鍵盤按鍵后,頁面會彈出一個數(shù)字,實際上鍵盤幾乎每個按鍵都有對應(yīng)的keyCode,因此keyCode可以很好地為我們檢測用戶按的是哪個鍵。現(xiàn)在我們來看一個通過keyCode用鍵盤控制div的移動的小例子:
<html> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <style> #div1 {width:100px; height:100px; background:#CCC; position:absolute;} </style> <script> document.onkeydown=function (ev) { var oEvent=ev||event; var oDiv=document.getElementById('div1'); if(oEvent.keyCode==37) { oDiv.style.left=oDiv.offsetLeft-10+'px'; } else if(oEvent.keyCode==39) { oDiv.style.left=oDiv.offsetLeft+10+'px'; } }; </script> </head> <body> <div id="div1"></div> </body> </html>
值得注意的一點是,如果我們按住一個方向鍵不動,可以看到div塊會先定住一會兒,然后才開始移動(實際上我們平時打字的時候也是這樣),這里給大家留一個懸念,大家可以自己思考一下如何解決這個問題。除了keyCode外,JS鍵盤事件還有幾個屬性:ctrlKey,altKey,shiftKey等。我們在論壇或微博經(jīng)常可以看到ctrl+回車提交留言的功能,就是通過這些屬性完成的:
<!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <title>無標(biāo)題文檔</title> <script> window.onload=function () { var oTxt1=document.getElementById('txt1'); var oTxt2=document.getElementById('txt2'); oTxt1.onkeydown=function (ev) { var oEvent=ev||event; if(oEvent.keyCode==13 && oEvent.ctrlKey) { oTxt2.value+=oTxt1.value+'\n'; oTxt1.value=''; } }; }; </script> </head> <body> <input id="txt1" type="text" /><br> <textarea id="txt2" rows="10" cols="40"></textarea> </body> </html>
oEvent.keyCode==13 && oEvent.ctrlKey代表的就是在按回車的同時按住ctrl鍵——這樣,我們就完成了一個ctrl+回車提交的功能。從下節(jié)課我們會講一些事件更加復(fù)雜,更加高級一些的應(yīng)用。
我是石川(Blue),如果你覺得我的公眾號還不錯,請多幫我推薦給你的朋友,多謝了。
作者簡介:前阿里巴巴高級技術(shù)經(jīng)理,現(xiàn)開課吧技術(shù)學(xué)院院長。精通C/C++、Java、Python、前端開發(fā)等多種開發(fā)技術(shù),曾參與淘寶網(wǎng)的早期建設(shè)和優(yōu)化,擁有豐富的企業(yè)級系統(tǒng)開發(fā)經(jīng)驗,對HTML5移動端互聯(lián)網(wǎng)技術(shù)及生態(tài)體系有深厚的造詣。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。