我们要明白:把CSS,JS放到单文件中这种代码层面的分离并不是各司其责,各司其责要做到职责的分离,比如我们要尽可能避免JS直接操作样式。
例子:Dark Mode
用JS实现浅色模式和深色模式的切换,如果是你,你会怎么做?
学成归来版
学完月影大大的JS课,复刻了一个常见的Dark Mode切换方式当作业,想看代码可以点这里,如果想知道怎么做,就继续往下看吧!
第一版
我们先写一段示例用的HTML:
新手可能会这样写JS来切换模式:
const btn = document.getElementById('modeBtn'); const body = document.body; btn.addEventListener('click', (e) => { if(e.target.innerHTML === '🌞') { body.style.backgroundColor = 'black'; body.style.color = 'white'; e.target.innerHTML = '🌜'; } else { body.style.backgroundColor = 'white'; body.style.color = 'black'; e.target.innerHTML = '🌞'; } }); 复制代码
这样写看似逻辑很清晰,但是JS代码直接操作了HTML和CSS,显然没有做到各司其责,而且代码可读性不是很好,第一时间并不能让人分辨出这是切换深色模式的代码。
第二版
第二版我们先在CSS里封装一个深色类,通过::after
伪类添加HTML内容:
.night { background-color: black; color: white; transition: all 1s; } #modeBtn::after { content: '🌞'; } .night #modeBtn::after { content: '🌜'; } 复制代码
然后JS只用添加或者删除类名就可以实现样式切换,这也是我常用的方法:
const btn = document.getElementById('modeBtn'); btn.addEventListener('click', (e) => { const body = document.body; if (body.className !== 'night') { body.className = 'night'; } else { body.className = ''; } }); 复制代码
也可以用toggle()
简化一下,关于该函数的用法可以看MDN文档:
const btn = document.getElementById("modeBtn"); btn.addEventListener("click", (e) => { document.body.classList.toggle("night"); }); 复制代码
这样写没有直接操作页面样式,JS只需要修改元素的类名即可,同时类名night
又能让人很容易分辨出代码的用途,但这就是最好的解决方案了吗?月影大大告诉你,并没有,我们还有最终版!
最终版
只要我不写代码,就永远不会有Bug!
我们先来思考一个问题,既然我们只是单纯的展示页面,我们能不能不用JS呢?单纯的CSS能不能实现这个切换功能呢?答案是当然可以。
CSS中checkbox
复选框就有checked选中和未选中两种状态,正好可以用来实现状态的切换。我们把复选框隐藏,然后用label显示🌞🌜:
<input id="modeCheckBox" type="checkbox"> <div class="content"> ... <label id="modeBtn" for="modeCheckBox"></label> ... </div> <style> #modeCheckBox { display: none; } #modeCheckBox:checked + .content { background-color: black; color: white; transition: all 1s; } #modeBtn::after { content: '🌞'; } #modeCheckBox:checked + .content #modeBtn::after { content: '🌜'; } </style> 复制代码
重剑无锋,大巧不工。我们应该做到:
- HTML/CSS/JS 各司其责
- 避免不必要的由JS直接操作样式
- 用class来表示状态
- 纯展示类的页面交互寻求零JS方案