整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

IT学堂:纯CSS的瀑布流布局方法总结

IT学堂:纯CSS的瀑布流布局方法总结

布流布局出来好多年了,刚出来时,一般都采用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瀑布流布局法:

CSS3 column多列可以非常轻松的实现瀑布流效果,它会自动将列表项分配到根据设置的列数当中去,先看下它的常用属性:

  • column-count:列数 给waterfall加
  • column-width:列的宽度
  • column-gap:列之间的间隙
  • break-inside:avoid 禁止列表项断裂,即左边列表项的内容有可能跑到右边的最上面 给item加

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竖排瀑布流布局法

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学堂,会有更多惊喜哦!

本文简介

  • 面试大厂,HTML/CSS,JS,网络基础这三块硬知识是必不可少的
  • 我整理了一些备考笔记,分享给大家
  • 初中级前端到高级前端的蜕变,从基础知识开始~

2 块元素和行元素

2.1 请说出3个H5新增的块元素,并介绍他们的应用场景

  • aside:表示article元素内容之外,与article元素内容相关的辅助信息
  • figure:代表一个块级图像,包含说明。figure添加标题时,与figcaption元素结合使用。
  • dialog:表示几个人直接的对话。与dt和dd元素结合使用,dt表示讲话者,dd表示讲话内容。

2.2 行内元素和块元素的区别

  • HTML元素分为行内、块状、行内块元素三种。
  • 块元素和行内块元素可以设置宽高
  • 行内元素不可以,高度由内容撑开
  • 三者是可以通过display属性任意转换的 block 块状元素 inline-block行内块元素 inline 行内元素

2.3 块级元素特征

  • 可以设置宽高
  • 设置margin、padding的上下左右都有效
  • 元素独占一行
  • 多个块状元素一起写,默认排列从上至下

我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年我花了一个月整理了一份最适合2020年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。

2.4 行内块元素特征

  • 能设置宽高
  • 不会自动换行
  • 多个行内块一起写,默认从左至右排列

2.5 行内元素特征

  • 设置宽高无效
  • 设置margin的上下无效,左右有效,设置padding上下左右都有效(并且会撑大空间)
  • 不会自动换行

3 CSS

3.1 请说出3个CSS浏览器前缀

  1. -ms- 兼容IE浏览器
  2. -moz- 兼容firefox
  3. -o- 兼容opera
  4. -webkit- 兼容chrome和safari

使用demo

div {  -ms-transform: rotate(0);  -webkit-transform: rotate(0);  -moz-transform: rotate(0);  -o-transform: rotate(0);  transform: rotate(0);}

CSS前缀自动补全:autoprefixer

3.2 盒模型

  1. 盒模型分为标准盒模型和怪异盒模型(IE5.X和6)两种
  2. 标准盒模型 width=content
  3. 怪异盒模型 width=content + padding + border

已知: margin:20px; border:10px; padding:10px; width:200px; height:50px;

![](https://imgkr2.cn-bj.ufileos.com/5b02ca29-357c-41bd-b193-925fbf089562.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=N8j%252FT85l87RtNBPpgYnJ1lYCWIo%253D&Expires=1599734594)

标准盒模型: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

![](https://imgkr2.cn-bj.ufileos.com/7779f321-279b-4c46-b6e5-b319a28b8549.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=iY2y7oaq2dl4L39xDbN0PO%252Bd4QI%253D&Expires=1599734605)

怪异盒模型:1\. 占用宽:margin*2+width=20*2+200=2402\. 占用高:margin*2+height=20*2+50=903\. 盒子实际宽度:width=2004\. 盒子实际高度height=50

IE8及更早版本不兼容问题解决方案:在HTML页面声明

3.3 box-sizing都有哪些值?他们的宽高分别如何计算?

  1. content-box(标准盒模型)|border-box(怪异盒模型)|inherit(继承父元素)
  2. content-box的宽高由content决定
  3. border-box的宽高由content+padding+border决定


4. BFC

4.1 什么是边界塌陷(或边界重叠)?

  1. 兄弟div(上下margin塌陷)
  2. 父子div(如果父级div没有padding\border\inlinecontent, 子级div的margin会向上查找边界塌陷的div,直到找到某个标签包括border\padding\inline content的其中一个,然后按此div进行margin)

4.2 什么是BFC(格式化上下文)?如何实现?

BFC(块状格式化上下文,独立的盒子,布局不受外部影响,但是如果同一个BFC中,同级块状元素的margin-top和margin-bottom会重叠)

只要元素满足下面的任一条件,都会触发BFC特征。

  1. body根元素
  2. position: fixed|absolute(绝对定位元素)
  3. float(浮动元素) 除了none
  4. overflow: hidden|auto|scroll (除了visible之外的值)
  5. display: inline-block\ table-cells\flex (具有table-的属性)

解决问题:

  1. 解决边界塌陷问题
  2. 解决浮动元素导致父元素高度塌陷问题
  3. 解决阻止元素被浮动元素覆盖问题


4.3 多个inline元素之间会有空隙,为什么?如何解决?

元素被当成行内元素排版时,元素直接的空白符会被浏览器处理,根据white-spack的处理方式(默认是normal,合并多余空白),Html代码在回车换行时被转成一个空白符,在字体不为0的情况下,空白符占据一定宽度,所以inline-block元素之间就出现了空隙。

复现<ul>  <li>首页</li>  <li>登陆</li>  <li>资源</li>  <li>社区</li>  <li>帮助</li></ul>

![](https://imgkr2.cn-bj.ufileos.com/50a6afa3-421d-4af9-9fad-48d72e69feb3.jpeg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=fkwoshYKbb6i%252FNOezztndQzAZPk%253D&Expires=1599740520)

解决办法:

  1. 多个inline元素写在同一行
  2. 将父级元素的font-size:0
  3. 给inline元素添加float的样式
  4. 设置子元素的margin为负值
  5. 设置父元素display:table和word-spacing:-1em

5 浮动

5.1 浮动会出现什么问题?

  • 浮动会导致父元素高度塌陷
  • 会覆盖其他元素

5.2 如何清除浮动?

  • 父元素加上overflow:hidden
  • 在浮动元素的后面(同级),添加一个div,属性是clear:both
  • 在父元素添加一个伪元素 .clearfix:after { display: block; height: 0; visibility: hidden; clear: both; content: '';} .clearfix{ //IE6模式下 zoom:1;}
  • 在父元素添加双伪元素 .clearfix:before,.clearfix:after { display: table; content: '';}.clearfix:after { clear: both;}.clearfix { zoom: 1; //兼容IE6下}

6 position都有哪些值?

  • relative 相对定位 (相对元素在文档中的初始位置定位)
  • absolute绝对定位(相对于定位元素定位,最顶级是body)
  • fixed 固定定位 (相对窗口定位)
  • static 文档流

![](https://imgkr2.cn-bj.ufileos.com/6ddb8a2b-1261-4b84-ae49-ec32c3c4b59a.webp?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=B7HTGqcB2LRMUjsAlcvjkLzn03s%253D&Expires=1599739510)

7 CSS的选择器

7.1 CSS的选择器有哪些?

  • id 选择器
  • class 选择器
  • tag 选择器
  • 属性 选择器(a[href=""] )
  • 派生 选择器

7.2 优先级高低如何判断?

  • 不同级优先级: !important>内联>id>class=属性=伪类 >标签=伪元素 > 通配符(*)
  • 同一级别: 后来居上 (后写覆盖先写) 内联样式 > 内部样式表 > 外部样式表 > 导入样式 @import ![](https://imgkr2.cn-bj.ufileos.com/67305866-9d2f-4f5c-9761-d42ee5ec7848.jpeg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=%252FKfOJPBRWgQIyPnGTYRpw8J4f%252BQ%253D&Expires=1599739287)

优先级相同时会发生什么? 样式被覆盖

7.3 列举几条常用的CSS reset

  • *{ margin:0; padding:0;}
  • ol, ul { list-style:none;}
  • body {line-height:1;}

7.4 是否了解normalize?

normalize.css是一个css reset的替代方案。

  • 保护有用的浏览器默认样式而不是完全去掉
  • 一般化样式:为大部分html元素提供
  • 修复浏览器自身的bug
  • 优化css可用性
  • 解释代码

7.5 如何做到一个list中奇数和偶数行的背景色不一样?

 ul>li:nth-child(2n+1) {    background-color: red; }ul>li:nth-child(2n) {    background-color: yellow;}

7.6 如何做到一个list的第一行没有上边框?

 ul>li:first-child {      border-top: none;  }


有梦想的人,眼睛会发光。

希望屏幕前的你,能够关注我一波。接下来,我会分享前端各种干货,以及编程中的趣事。

天这篇文章我们开始详细讲解JS的事件。之前我们已经讲过许多事件,而我们今天要讲的主要是事件对象。之前我们学习的onclick事件用于得知一个元素是否被点击,但如果我们想知道一些更详细的信息,例如点击位置,点击键是左键还是右键,就需要用到event对象。又例如我们希望检测用户键盘按的到底是左键还是右键来完成相应的功能,也需要用到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移动端互联网技术及生态体系有深厚的造诣。