写个脚本保护视力
WoodenStone

用 JS 给网页加个深色模式保护眼睛(和一个失败的实现)。

起因

最近在看的一些技术文档都是用VuePress 写的,虽然很简洁好看,但是大部分网页都是白屏黑字,对于一个每天除了睡觉就是盯着电子屏幕的人来说,#fff的背景色配上黑色小字实在是太伤眼睛了。这我大JS不就派上用场了?为了保护视力,写个脚本加个深色模式吧!

开干

首先分析下页面结构,主页面是个叫pageclass,侧边栏是个叫sidebarclass,那么很简单,直接用qeurySelector改改颜色就行了:

1
2
3
document.querySelector(".page").style.backgroundColor = '#111';
document.querySelector(".page").style.color = '#ccc';
document.querySelector(".sidebar").style.backgroundColor = '#111';

改完之后的效果:

果然舒服多了。而且由于前端路由的特性,页面变化相当于在document这个容器中装各种各样的内容,这样切换到别的页面也还是深色模式。但是技术文档的特点就是有很多行内代码块,在VuePress中颜色是#476582,深色背景下就不是很友好,比如:

这也很简单,行内代码都是用<code></code>包裹的,那直接选中改色不就完了:

1
2
const codeList = Array.from(document.querySelectorAll("code"));
codeList.forEach(item => {item.style.color = "#54f36f";});

大功告成,现在的效果是这样的:

但是,当我们跳转到另一个页面,行内代码的颜色又变回去了🤒

因此,新的问题就是:怎样在路由变化时也能让行内代码变为想要的颜色呢?

根据Vue Router的两种实现方式:Hash Router和History Router,可以很容易的想到只要监听路由变化,在路由变化的时候改个颜色就行了呀!如果是Hash Router,就非常简单,直接监听hashchange事件就行了。但是很多网站为了优雅,都是使用的History Router模式(但我怀疑使用Hash Router是不是没法用锚点链接和copy link to highlight了?在自己的站点上测试了下貌似没法用)。

复习下History Router的实现方式,主要就是利用History API

  • pushStatereplaceState来实现页面内容的更新和会话历史栈的更新
  • popState来监听前进后退等事件

pushStatereplaceState是相辅相成的,前者记录历史,后者切换url,这样点击浏览器的前进后退时也能滚动到锚点位置,而且有好看的滚动动画。那么监听哪个事件呢?

从使用场景上来想,如果是在一个文档中有不同的标题,那么进入文档时,首先进行pushState记录历史(url),然后使用replaceState定位到当前的锚点链接(标题),在滑动浏览的过程中,不断使用replaceState来更新当前的锚点链接,而直接点击某个小标题时,则使用pushState保证回退时可以回退到正确位置。这样看来应该是使用pushState执行的次数会少一些。

接下来就开始实现了。由于个人使用,不用考虑兼容性什么的问题(日常用IE那是不可能的),直接用ES6的proxy做代理即可:

1
2
3
4
5
6
7
8
9
history.pushState = new Proxy(history.pushState, {
apply: function (target, thisBinding, args) {
const codeList = Array.from(document.querySelectorAll("code"));
if(codeList[0].style.color !== "rgb(84, 243, 111)") {
codeList.forEach(item => {item.style= "color: #54f36f";});
}
return target.apply(thisBinding, args);
},
});

判断是为了减少反复更改的次数。不过这么写还有一个问题,就是如果从侧边栏进入到另一个文档,第一次视图不会更新。这个问题不知道该怎么解决,我目前的做法是手动再push一遍,如下:

1
2
3
4
5
6
7
8
9
10
history.pushState = new Proxy(history.pushState, {
apply: function (target, thisBinding, args) {
const codeList = Array.from(document.querySelectorAll("code"));
if(codeList[0].style.color !== "rgb(84, 243, 111)") {
codeList.forEach(item => {item.style= "color: #54f36f";});
}
window.pushState(window.location.href) // 加上这一句
return target.apply(thisBinding, args);
},
});

这样会导致得回退两次才能退回去,且加载时会有一闪而过的白屏。

接着给window添加一个onload事件,让第一次进入页面时代码就亮起来:

1
2
3
4
window.onload = function() {
const codeList = Array.from(document.querySelectorAll("code"));
codeList.forEach(item => {item.style.color = "#54f36f";});
}

最后是侧边栏,不过我觉得侧边栏不是很有必要,稍微写一下:

1
Array.from(document.querySelectorAll(".sidebar-link:not(.sidebar-link.active)")).forEach(item => {item.style.color="#aaaaaa"})

最终效果:

最后

为了保护眼睛折腾了好半天,最后实现的效果还不尽如人意,健康真费劲啊。从结果上说,可能看屏幕一小时就出去晒晒太阳是更经济的选择。不过借这个突发奇想复习了下history router,也挺好的。

  • 本文标题:写个脚本保护视力
  • 本文作者:WoodenStone
  • 创建时间:2021-12-19 13:28:51
  • 本文链接:https://woodenstone.github.io/Whim/create-my-dark-mode/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!