React 中实现一个 todo 打勾效果

咕咕咕~鸽子精回来了,自从新入职了一家实习之后人都要累鼠了,基本没什么功夫去更新文章;还有一个原因是没什么选题。

正好呢,最近做的一些东西让我燃起了一些些记录的热情,不能再咕咕了。

背景是我自己做的 landing 项目里有一个实现类似飞书里的 todo 的效果,然后我研究了下飞书,写出来了一个 demo。

我们先来提出几个问题,解决了之后,我们也就实现了一个todo

  1. 前面的 icon 用什么来实现。
  2. 如何实现文字不可点击,前方icon可点击的效果?
  3. 如果有多个元素循环渲染的情况下,我们怎么在点击的时候对单个元素做样式的修改?(React)

这里有几个部分需要来讨论:

首先是整个用什么来实现这个前面的 icon,是使用伪元素,还是使用一个真实的元素?

这里我看飞书直接用的伪元素来实现的,感觉可能是能用 css 解决的问题就没有必要再去用 html 去写了,那么大致实现逻辑如下。

html 复制代码
 <div class="todo" id="todo">11111</div>

我们使用伪元素实现这个样式的时候,左侧并不会撑开,也就是说,我们需要去给这个 div 一个靠左的 margin 值。然后再去做具体的打勾框。

css 复制代码
    .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 的时候会让伪元素的样式变蓝。于是增加了如下代码,有了下图所示的效果

css 复制代码
    .todo:hover:before {
        border: 1px solid rgb(100, 149, 237);
    }

第二个点就是,我们如何实现一个文字不可点击,前方icon可点击的效果呢?

其实在上面的代码中已经展示出来了,为这个 div设置pointer-events: none;,然后给伪元素设置pointer-events: auto;就可以啦。

这些考虑完之后,我再给你们展示一下点击状态的css咋写的:

css 复制代码
    .done {
        text-decoration: line-through;
    }

    .done::before {
        background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEwLjU4OSAzLjkwM2wuODA4LjgwOGEuMzUuMzUgMCAwMTAgLjQ5NUw2LjE4IDEwLjQyNWEuMzUuMzUgMCAwMS0uNDk1IDBMMi43MDMgNy40NDRhLjM1LjM1IDAgMDEwLS40OTVsLjgwOC0uODA4YS4zNS4zNSAwIDAxLjQ5NSAwbDEuOTI1IDEuOTI0IDQuMTYzLTQuMTYzYS4zNS4zNSAwIDAxLjQ5NSAweiIgZmlsbD0iIzMzNzBGRiIvPjwvc3ZnPg==);
    }

那么你给这个 div 再加上 done 的类名,就可以得到下图的效果了:

最后呢,如果我们有多个 div,我们如何去做类名的处理呢?

答案是监听点击事件,然后在点击的时候更换类名,代码如下:

js 复制代码
    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'
        }
    })

完整代码如下:

html 复制代码
<!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>

好啦,那么这就是本期文章的全部内容。

这里是不更新就在打工的阳树,我们下次再见~

相关推荐
虾球xz8 分钟前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇13 分钟前
HTML常用表格与标签
前端·html
疯狂的沙粒17 分钟前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员33 分钟前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐35 分钟前
前端图像处理(一)
前端
程序猿阿伟42 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒44 分钟前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪1 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背1 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M1 小时前
node.js第三方Express 框架
前端·javascript·node.js·express