我们先来提出几个问题,解决了之后,我们也就实现了一个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>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div 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>