基于React实现一个todo打勾效果
作者:阳树阳树
正好呢,最近做的一些东西让我燃起了一些些记录的热情,不能再咕咕了。
背景是我自己做的 landing 项目里有一个实现类似飞书里的 todo 的效果,然后我研究了下飞书,写出来了一个 demo。
我们先来提出几个问题,解决了之后,我们也就实现了一个todo
- 前面的
icon
用什么来实现。 - 如何实现文字不可点击,前方
icon
可点击的效果? - 如果有多个元素循环渲染的情况下,我们怎么在点击的时候对单个元素做样式的修改?(React)
这里有几个部分需要来讨论:
首先是整个用什么来实现这个前面的 icon,是使用伪元素,还是使用一个真实的元素?
这里我看飞书直接用的伪元素来实现的,感觉可能是能用 css
解决的问题就没有必要再去用 html
去写了,那么大致实现逻辑如下。
<div class="todo" id="todo">11111</div>
我们使用伪元素实现这个样式的时候,左侧并不会撑开,也就是说,我们需要去给这个 div
一个靠左的 margin 值。然后再去做具体的打勾框。
.todo { pointer-events: none; margin-left: 20px; } .todo:before { content: " "; display: inline-block; line-height: normal; font-size: 16px; border: 1px solid black; border-radius: 4px; background-position: 50%; white-space: normal; width: 16px; height: 16px; margin-right: 8px; margin-left: -24px; box-sizing: border-box !important; position: relative; cursor: pointer; pointer-events: auto; top: 2px; }
那么上面这些代码,就可以得到下图的展示效果:
是不是看起来已经有了完整的样子啦。
这是我们的 todo
状态,我们还需要实现一个 done
的状态,所以还需要额外的 css
去书写,这里需要注意的是,为了交互更加好看,我们在 hover
的时候会让伪元素的样式变蓝。于是增加了如下代码,有了下图所示的效果
.todo:hover:before { border: 1px solid rgb(100, 149, 237); }
第二个点就是,我们如何实现一个文字不可点击,前方icon
可点击的效果呢?
其实在上面的代码中已经展示出来了,为这个 div
设置pointer-events: none;
,然后给伪元素设置pointer-events: auto;
就可以啦。
这些考虑完之后,我再给你们展示一下点击状态的css
咋写的:
.done { text-decoration: line-through; } .done::before { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEwLjU4OSAzLjkwM2wuODA4LjgwOGEuMzUuMzUgMCAwMTAgLjQ5NUw2LjE4IDEwLjQyNWEuMzUuMzUgMCAwMS0uNDk1IDBMMi43MDMgNy40NDRhLjM1LjM1IDAgMDEwLS40OTVsLjgwOC0uODA4YS4zNS4zNSAwIDAxLjQ5NSAwbDEuOTI1IDEuOTI0IDQuMTYzLTQuMTYzYS4zNS4zNSAwIDAxLjQ5NSAweiIgZmlsbD0iIzMzNzBGRiIvPjwvc3ZnPg==); }
那么你给这个 div
再加上 done
的类名,就可以得到下图的效果了:
最后呢,如果我们有多个 div
,我们如何去做类名的处理呢?
答案是监听点击事件,然后在点击的时候更换类名,代码如下:
const div = document.getElementById('todo'); div.addEventListener('click', (target) => { const classArr = target.currentTarget.className.split(' '); if (classArr.length > 1) { target.currentTarget.className = 'todo' } else { target.currentTarget.className = 'todo done' } })
完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div class="todo" id="todo">11111</div> </body> <script> const div = document.getElementById('todo'); div.addEventListener('click', (target) => { const classArr = target.currentTarget.className.split(' '); if (classArr.length > 1) { target.currentTarget.className = 'todo' } else { target.currentTarget.className = 'todo done' } }) </script> <style> .todo { margin-left: 20px; } .todo:before { content: " "; display: inline-block; line-height: normal; font-size: 16px; border: 1px solid black; border-radius: 4px; background-position: 50%; white-space: normal; width: 16px; height: 16px; margin-right: 8px; margin-left: -24px; box-sizing: border-box !important; position: relative; cursor: pointer; pointer-events: auto; top: 2px; } .todo:hover:before { border: 1px solid rgb(100, 149, 237); } .done { text-decoration: line-through; } .done::before { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEwLjU4OSAzLjkwM2wuODA4LjgwOGEuMzUuMzUgMCAwMTAgLjQ5NUw2LjE4IDEwLjQyNWEuMzUuMzUgMCAwMS0uNDk1IDBMMi43MDMgNy40NDRhLjM1LjM1IDAgMDEwLS40OTVsLjgwOC0uODA4YS4zNS4zNSAwIDAxLjQ5NSAwbDEuOTI1IDEuOTI0IDQuMTYzLTQuMTYzYS4zNS4zNSAwIDAxLjQ5NSAweiIgZmlsbD0iIzMzNzBGRiIvPjwvc3ZnPg==); } </style> </html>
到此这篇关于基于React实现一个todo打勾效果的文章就介绍到这了,更多相关React todo打勾内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!