CSS 怎么用来管理 JS 事件吗? 下面本篇文章就来给大家介绍一些借助 CSS 来管理 JS 事件的案例. 有一定的参考价值, 有需要的朋友可以参考一下, 希望对大家有所帮助.
CSS 是一门很神奇的语言, 很多和它不相干的功能却能起到很显著的效果, 有些在 JS 看起来实现都有一定的工作量, CSS 一句属性就能轻而易举的解决, 下面来看几个例子 (主要和 JS 事件相关).
原链接
https://notes.codelabo.cn/article/4
原 issues
https://github.com/XboxYan/notes/issues/4
很多都是开脑洞想出来的, 实现效果却意外的惊人
事件禁用
在 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 https://codepen.io/xboxyan/pen/KLGXjR
长按事件
原生 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 https://codepen.io/xboxyan/pen/KLxXNZ
单次点击事件
在 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 https://codepen.io/xboxyan/pen/ZNqrbX
onresize 事件
大家都知道 Windows 有个 onresize 事件, 可以在窗口拉伸的时候触发, 然后就能实时获取窗体的尺寸等等属性.
- Windows.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 https://codepen.io/xboxyan/pen/PvyeOL
小节
以上通过借助 CSS, 实现了许多 JS 都很棘手的问题, 可能还会有更多的应用场景, 欢迎小伙伴留言讨论~
本文转载自: https://segmentfault.com/a/1190000019342789
更多 web 前端开发 https://www.html.cn/ 知识, 请查阅 HTML 中文网 !!
来源: http://www.css88.com/web/css/16161.html