CSS 是一门很神奇的语言,很多和它不相干的功能却能起到很显著的效果,有些在 js 看起来实现都有一定的工作量,CSS 一句属性就能轻而易举的解决,下面来看几个例子(主要和 js 事件相关)。
原链接
https://notes.codelabo.cn/art…
原 issues
https://github.com/XboxYan/no…
很多都是开脑洞想出来的,实现效果却意外的惊人
事件禁用
在 js 中对事件禁用并不复杂,但是却容易影响到业务逻辑
function buy(){if(XX){return false;}
// 其他业务代码
}
当然,元素也要设置相应的样式,让它看起来不可点击。
css 对事件禁用就比较简单了,主要有两种方式
1. disabled
原生表单元素是有默认的禁用属性的,比如
<button disabled onclick="alert(11)"> 按钮 </button>
可以看出,禁用的默认样式为
button:disabled {color: graytext;}
所以,在这里你可以随意的修改禁用的样式
button:disabled {color: red;}
button[disabled] {/** 属性选择器也行 **/
color: red;
}
button:hover { /**:hover 支持 **/
color: red;
}
button:active,button:focus{/** 不支持,其实也好理解,会触发 focus()事件,所以也禁用了 **/
color:red
}
一般情况下,使用这种方式是比较好的,天然的禁用属性,兼容性也不错
2. pointer-events:none
这个属性应该也不陌生,最常见的用法就是禁用一个按钮,而且不局限于表单元素,任意元素均可(比如很多人喜欢用的 a 标签)
button.disabled {
pointer-events:none;
user-select:none;/* 去除选中效果 */
color: graytext;
}
这个属性用处很多,很多 js 绞尽脑汁想要过滤掉的方法,直接就用一行属性解决,这里就不展开了,网上教程很多。
查看这个 demo
<iframe height=”300″ style=”width: 100%;” scrolling=”no” title=”css 禁用事件 ” src=”//codepen.io/xboxyan/embed/KLGXjR/?height=300&theme-id=34022&default-tab=html,result” frameborder=”no” allowtransparency=”true” allowfullscreen=”true”>
See the Pen css 禁用事件 by XboxYan
(@xboxyan) on CodePen.
</iframe>
长按事件
原生 js 中并没有长按事件,通常开发者一般会顺着思路,使用定时器来完成
下面是伪代码
el.onmousedown = function(){this.timer && clearTimeout(this.timer);
this.timer = settimeout(function(){// 业务代码},350)
}
el.onmouseup = function(){this.timer && clearTimeout(this.timer);
}
当然,可以借助 css 来完成,而且效果更好,易于控制
这里为什么说是“借助”呢,因为不能完全有 css 来完成,只是利用了某些特性
css3 中新增了过渡和动画属性,与之相对应的也预设了这些动画的回调事件,如下
事件 | 说明 |
---|---|
transitionstart |
在开始过渡时触发 |
transitionrun |
在进行过渡时触发 |
transitioncancel |
在取消过渡时触发 |
transitionend |
在完成过渡后触发 |
animationstart |
在 animation 开始时触发 |
animationiteration |
在 animation 完成一个周期时触发 |
animationend |
在 animation 完成时触发 |
animationcancel |
在 animation 取消时触发 |
有些事件存在兼容性问题,有兴趣的可以详细研究
有了这些事件,要做一个长按事件就很容易了,这里有两种种思路,大家可以脑洞一下
假设需要延时 1s;
- 过渡时间设置为 1s,调用
transitionend
或者animationend
- 延时 1s,调用
transitionstart
或者animationstart
这里以第一种情况transitionend
(过渡比动画实现要容易多,代码也少,优先用过渡)来实现
button:hover:active{
opacity:.99;/** 随便选取一个不影响页面的可以过渡的样式 **/
transition:opacity 1s;
}
相应的,js 需要监听 transitionend
事件,不需要借助定时器(个人有点鄙视定时器的思想 ^)
el.addEventListener('transitionend',function(){// 业务代码})
是不是精简很多呢,需要改长按时间可以直接通过 css 来控制
这里封装了一下自定义事件,可以更好的在项目中使用(虽然代码本身就很少了)。
查看这个 demo
<iframe height=”300″ style=”width: 100%;” scrolling=”no” title=”css 长按事件 ” src=”//codepen.io/xboxyan/embed/KLxXNZ/?height=300&theme-id=34022&default-tab=css,result” frameborder=”no” allowtransparency=”true” allowfullscreen=”true”>
See the Pen css 长按事件 by XboxYan
(@xboxyan) on CodePen.
</iframe>
单次点击事件
在 jquery
中有一个once
(好像是这个)方法,表示绑定一次性事件,点击一次后就不再生效。
原生 js 实现这个也不复杂,通常做法就是定义一个标识,点击后改变一下,下面是伪代码
el.onclick = function(){if(!this.once){
// 业务代码
this.once = true;
}
}
借助上面的思路,css 也可以实现类似的效果,深究了一番,总结以下两个方法
1.animationend
animationend
是动画结束后触发,显然我们可以在这里做文章
比如我们可以在初始状态给一个暂停状态animation-play-state:paused
,然后在点击时运动animation-play-state:running
,结束后一直保持在最后状态animation-fill-mode: forwards
.button{animation: once .01s paused forwards;/** 给一个足够小的运动时间,确保能够在 `:active` 时运动完成 **/}
.button:hover:active{animation-play-state:running;}
@keyframes once {
to{opacity:.8;}
}
js 只需要对 animationend
进行监听
el.addEventListener('animationend',function(){// 业务代码})
2. 纯 css 实现
不知道在上面的例子中有没有发现,在动画结束时把元素禁用会怎么样?
@keyframes once {
to{
opacity:.8;
pointer-events:none;
user-select:none;
}
}
<button class="button" onclick="fn()"> 按钮 </button>
这样就可以在运动完直接禁用,好像很完美?
事实上,这里运动的时间很难把控,大家都知道,onclick
其实是包含按下和抬起两个过程的,如果抬起的太慢,那么此时元素已经禁用了,触发不了事件;如果抬起太快,如果快速点击,由于还没运动完成,会触发多次事件。
解决方式也很简单,用 onmousedown
代替即可
<button class="button" onmousedown="fn()"> 按钮 </button>
查看这个 demo
<iframe height=”300″ style=”width: 100%;” scrolling=”no” title=”css 单次事件 ” src=”//codepen.io/xboxyan/embed/ZNqrbX/?height=300&theme-id=34022&default-tab=css,result” frameborder=”no” allowtransparency=”true” allowfullscreen=”true”>
See the Pen css 单次事件 by XboxYan
(@xboxyan) on CodePen.
</iframe>
onresize 事件
大家都知道 window
有个 onresize
事件,可以在窗口拉伸的时候触发,然后就能实时获取窗体的尺寸等等属性。
window.onresize = function(ev){// 业务代码}
但是,普通的元素却没有这个监听,比如
div.onresize = function(ev){// 不生效...}
为什么需要这个功能呢?
大家可能知道有这样一个属性,可以原生拉伸元素,改变尺寸
.box{
overflow: hidden;/** 需要配合 overflow 才能生效 **/
resize: both;
}
只需一个属性,就可以实现元素的拉伸(虽然稍有瑕疵),不需要大量的 js 来计算。
视觉展示还好,但是如果需要实时知道元素的尺寸怎么办呢,js 通常有两种思路
1. onmousemove 事件
拉伸的时候很自然的想到是有鼠标按住然后拖拽完成,所以可以给元素添加 onmouse-*
一系列事件,同时要注意鼠标抬起要取消监听
// 大概是这样的逻辑
div.onmousedown = function(){
div.onmousemove = fn;// 监听
div.onmouseup = function(){div.onmousemove = null;}
}
2. 定时器
借助 setInterval
或者 requestAnimFrame
来实现监听,同样是需要注意取消监听的时机
// 大概是这样的逻辑
div.onmousedown = function(){this.timer && clearInterval(this.timer)
this.timer = setInterval(fn,300);// 监听
div.onmouseup = function(){this.timer && clearInterval(this.timer)
}
}
很显然,上面的方法都不是特别的舒适,很多情况下都会出现监听取消的问题,导致不停的触发,影响体验。
这时候,又轮到 css 出场了,很多时候 js 觉得实现起来不顺畅的时候都可以用 css 的思维来重新认识。
那么,如何借助 css 来监听这些呢?
可以从过渡和动画两个思路来考虑。
1. CSS 过渡
首先,我们可以知道的是,在通过 resize: both
进行元素拉伸的时候,改变的是 width
和height
,这一点可以从开发者工具直接看到
我们需要通过 transitionrun
或者 transitionend
来监听 width
和height
,所以需要给这两个属性加上过渡
.box{transition:width .3s,ehight .3s;// 分别给 width 和 height 设置过渡,其他属性不需要}
这样就可以拿到监听了,不过这种方式有一个弊端,由于过渡需要时间,所以有一种不跟随,卡顿的感觉。
所以我们来看第二种方式
2. animationiteration
css
动画可以设置播放次数,如果设置成 animation-iteration-count: infinite
就表示无限轮播,配合 animationiteration
回调,不就可以实现监听了么
.box:active{animation: resize .3s infinite forwards;}
@keyframes resize{
to {opacity: .99;/** 用一个无关紧要的属性来触发动画 **/}
}
js 很简单,一个监听就能解决问题,也不需要什么取消监听什么,这些都已经在 css 完成了
el.addEventListener('animationiteration',function(){// 业务代码})
这里是每个动画完成一次就回调一次,所以我们可以通过设置动画时长来控制监听的频率,比如上面是 .3s
的触发频率。
下面写了一个 demo,可以实现自定义 onresize
事件
查看这个 demo
<iframe height=”300″ style=”width: 100%;” scrolling=”no” title=”css resize 事件 ” src=”//codepen.io/xboxyan/embed/PvyeOL/?height=300&theme-id=34022&default-tab=css,result” frameborder=”no” allowtransparency=”true” allowfullscreen=”true”>
See the Pen css resize 事件 by XboxYan
(@xboxyan) on CodePen.
</iframe>
小节
以上通过借助 css,实现了许多 js 都很棘手的问题,可能还会有更多的应用场景,欢迎小伙伴留言讨论~