布流布局出来好多年了,刚出来时,一般都采用js来实现,涌现了很多比较好的js插件,例如:masonry.js、gridify.js等等。这里就不再详说如何使用了,网上都能找到。今天讨论的是纯CSS实现瀑布流布局。
这种布局方法非常简单,就是将多个列表横向排列,每个列表拥有的项都差不多。图片宽度固定,高度自动撑起来。
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 都是从上往下排列的,并不能实现从左往右,解决办法是:在获取数据时,需要将数据按奇偶进行分类显示(假设显示两列,多列同理)。您可以定义两个数组:listEven和listOdd,分别来接受获取的数据的偶数项和奇数项,然后分别进行渲染。这里用VUE来演示:
//假设res.data为ajax请求到的数据 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多列可以非常轻松的实现瀑布流效果,它会自动将列表项分配到根据设置的列数当中去,先看下它的常用属性:
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 都是从上往下排列的,并不能实现从左往右,第一种方法采用了奇偶两个数组解决了,这个不好解决,如果带有上拉加载,不确定总个数,就更不好解决了。
2、item的上下间距尽量不要用margin,否则可能会出现下面这种情况,这是因为左列最后一个margin跑到右边上面去了,改成padding即可,因为padding属于item的内容,而break-inside: avoid则禁止item折断:
css3瀑布流布局
CSS3的flex用法很多。大多数情况都是用来横排,将块状容器显示在一行,还有就是用来对其一行。但是,改变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随着内容变多,不断撑高也不会换列。
3、waterfall的height要随着item的数量变化而变化,否则,一列显示不了就显示两列,两列显示不了就显示三列,为了维持固定的列数,只能不断升高waterfall的高度。
4、flex-direction: column和flex-wrap: wrap可以合并为flex-flow: column wrap;
5、与前面两种方法一样,这种方法仍然是不好解决从上往下排列而非从左往右排列的问题。
总结:纯CSS实现瀑布流,总有这样或那样的问题,最终还是要通过js来控制。如果对排序没有要求的话,第二种方法还是比较好用的。纯css实现瀑布流,大家还有什么好的方法,欢迎留言评论。关注IT学堂,会有更多惊喜哦!
我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年我花了一个月整理了一份最适合2020年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。
使用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;

标准盒模型: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会重叠)
只要元素满足下面的任一条件,都会触发BFC特征。
解决问题:
元素被当成行内元素排版时,元素直接的空白符会被浏览器处理,根据white-spack的处理方式(默认是normal,合并多余空白),Html代码在回车换行时被转成一个空白符,在字体不为0的情况下,空白符占据一定宽度,所以inline-block元素之间就出现了空隙。
复现<ul> <li>首页</li> <li>登陆</li> <li>资源</li> <li>社区</li> <li>帮助</li></ul>

解决办法:

优先级相同时会发生什么? 样式被覆盖
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; }
有梦想的人,眼睛会发光。
希望屏幕前的你,能够关注我一波。接下来,我会分享前端各种干货,以及编程中的趣事。
天这篇文章我们开始详细讲解JS的事件。之前我们已经讲过许多事件,而我们今天要讲的主要是事件对象。之前我们学习的onclick事件用于得知一个元素是否被点击,但如果我们想知道一些更详细的信息,例如点击位置,点击键是左键还是右键,就需要用到event对象。又例如我们希望检测用户键盘按的到底是左键还是右键来完成相应的功能,也需要用到event对象。
event对象的作用是帮助我们获取一些事件的信息,例如鼠标位置,键盘按键。这里我们用一个小例子来说明event对象是怎么运作的。现在我们试图实现一个效果:无论点击页面的什么地方,都可以弹窗。聪明的同学可能已经猜到,我们可以直接给body添加onclick事件来实现这一点:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title>无标题文档</title> <script>window.onload=function (){ document.body.onclick=function () { alert('a'); };}; </script> </head> <body> </body> </html>
但实际上你会发现,无论怎么点击页面都无法进行弹窗,这是为什么呢?现在我们对这个页面改造一下:
<html> <head> <meta charset="utf-8" /> <title>无标题文档</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内没有内容的时候,body页面是撑不起来的(至少在ie下是这样的),我们在页面中加入了一个button后,body的大小也随着button略微增大。当然我们可以从样式角度去解决这个问题,但是我们现在从程序的角度考虑的话,应该怎么去解决他呢?其实很简单,我们不选择给body添加onclick事件而是直接给document添加就可以了:
window.onload=function (){ document.onclick=function () { alert('a'); }; };
效果如下:
这里body也没有被撑起来,但是document代表的是整个网页,因此可以通过点击页面任何地方弹窗。这里我们通过一个例子来讲讲document到底是什么东西。
window.onload=function (){ alert(document.childNodes[0].tagName); };
效果如下:
这里本应该打印出document第一个子节点的标签名,但返回结果为undefined。顺便大家可以用IE7测试一下,其返回结果会为一个感叹号。聪明的同学应该可以猜到,感叹号其实就是DOCTYPE声明中的第一个字符,DOCTYPE声明本身也是作为一个节点存在的。
实际上我们可以这么理解:在一个页面里,DOCTYPE和html存在同一个父级,也就是document(一个看不见的虚拟节点),他包括了整个网页的内容和网页声明。我们提了这么多关于document的知识,为的就是告诉大家一点:如果想给整个页面添加事件,那么使用document一定比使用html好,因为html页面是有可能撑不开的。回到我们的事件对象来。我们通过event对象可以获取事件的信息,例如可以退获取点击的坐标:
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就可以获取点击的坐标,而在火狐下,event对象是不兼容的,需要给事件读入一个参数(参数本身就是事件对象,由系统读入),用这个参数来调用clientX和clientY属性获取坐标。而“var oEvent=ev||event;”这种写法,系统会自动获取为真的一边,这样就可以通过一句话完美解决兼容性的问题了。
说完事件对象后我们来看看事件流?事件流说的简单一点,其实就是事件跟水一样从头到尾流动的过程。我们来看一个关于事件流的最简单的小例子:
<!DOCTYPE HTML> <html onclick="alert('html');"> <head> <meta charset="utf-8"> <title>无标题文档</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>
效果如下:
当我们点击最里面的一个div时,它会连续弹出多个框,原因大家应该都明白。这里,就涉及到了事件冒泡的知识——在这个程序里,当我们点击了最里面的div并执行事件后,还会将这个事件传递给父级继续执行,依次类推,直到html和document。这就是所谓的事件冒泡——事件会随着层级依次传递到底。
事件冒泡学完以后大家更多的疑问肯定是,我们能用它来做点什么,毕竟学了任何知识都是拿来用的,而不是看的。一个小小的结论是:在我们平时做东西的时候,真正主动去利用时间冒泡去做的事情非常少,甚至可以说,很多时候事件冒泡都会给我们带来烦恼脑。为什么这么说呢,下面我们来看一个小例子,立刻就能明白了。
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>无标题文档</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>
我们试想一下,我们想要实现一个功能:点击页面任意地方,都可以让已经显示的div块隐藏,根据我们前面所学的知识,最好的方法应该是给document添加事件。但如果我们直接这么做的话,就会存在下面这个问题:我们在点击按钮的时候,这个事件也会被传递到其根元素document上,也就是说,点击了按钮后,会触发两个事件,div依然会被隐藏。取消冒泡的方式也是通过事件对象,在event对象里有一个属性叫cancelBubble,当我们将它设置为true的时候,就可以取消该元素的事件冒泡了。
下面我们来说一下鼠标事件,之前说过了,clientX,clinentY可以获取鼠标的坐标。不过,我们仅仅说它是鼠标的坐标,而并没有说它是鼠标的什么坐标。现在我们来看一下clientX,clinentY有一些小问题。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>无标题文档</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坐标就等于鼠标的当前坐标,可以看到我们实现了一个div随着鼠标移动的效果。但这个程序存在一个小小的隐患——例如,现在如果我们把body的高度设置为2000px,会发生什么呢?实际上,无论是clientX还是clientY,它们代表的实际含义都是可视区坐标,而div则是根据body来定位的,所以当可视区并不在页面顶端时,必然会出现鼠标和div之间的错位。这里,我们可以使用scrollTop来解决这个问题。
<html> <head> <meta charset="utf-8"> <title>无标题文档</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>
这样,无论是否滚动页面,都不会出现错位的问题了(不过会出现div抖动的情况,我们以后会有方法解决)。一个小小的经验是:但凡是你用到clientX,clientY的时候一定要加上scrollLeft或者scrollTop,否则很容易出问题。
现在我们来看一下其另一个例子(这里我们将scroll封装成一个函数getPos。
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>无标题文档</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跟随着鼠标走的特效。我们让后一个div都跟着前一个div走,第一个div跟着鼠标走,就可以实现这种效果了。
说完鼠标事件之后,我们来看看键盘事件。通过keyCode,我们可以获取用户操作的是哪个按键。这里我们需要介绍两个新事件:onkeydown和onkeyup,分别代表按下键盘按键和放开键盘按键的事件。
这里通过一个小例子来说明他们是怎么用的:
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> <script> document.onkeydown=function (ev) { var oEvent=ev||event; alert(oEvent.keyCode); }; </script> </head> <body> </body> </html>
可以看到的是,我们输入一个键盘按键后,页面会弹出一个数字,实际上键盘几乎每个按键都有对应的keyCode,因此keyCode可以很好地为我们检测用户按的是哪个键。现在我们来看一个通过keyCode用键盘控制div的移动的小例子:
<html> <head> <meta charset="utf-8"> <title>无标题文档</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等。我们在论坛或微博经常可以看到ctrl+回车提交留言的功能,就是通过这些属性完成的:
<!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <title>无标题文档</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+回车提交的功能。从下节课我们会讲一些事件更加复杂,更加高级一些的应用。
我是石川(Blue),如果你觉得我的公众号还不错,请多帮我推荐给你的朋友,多谢了。
作者简介:前阿里巴巴高级技术经理,现开课吧技术学院院长。精通C/C++、Java、Python、前端开发等多种开发技术,曾参与淘宝网的早期建设和优化,拥有丰富的企业级系统开发经验,对HTML5移动端互联网技术及生态体系有深厚的造诣。
*请认真填写需求信息,我们会在24小时内与您取得联系。