前言
在中文排版中,直角引号比弯引号更加美观(至少我是这么觉得的)。然而,在中国大陆,直角引号在横排中并不是规范标准,也没有广泛使用。即使是之前玩过一会《原神》的我,看到直角引号还是难免有点不习惯。我想在我的博客中使用直角引号,但直接一下子全换掉必定会影响大部分人的阅读体验。一个折中的方案是,提供一个开关,让用户根据自己的喜好自行选择使用什么引号。
以下的实现以我自己的博客(Hexo Butterfly)为例,你应该根据你自己情况做出适当调整。
基本实现
首先,我们需要一个变量来存储当前的引号状态。我找不到“直角引号”的英文名,这里,我将其译作“square quotes”:
1 2 3 4 5
|
let squareQuotes = false;
|
默认为 false,即使用弯引号,以符合大多数人的习惯。
然后,先实现切换一个元素内的引号的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const charPairs = [ ['“', '「'], ['”', '」'], ['‘', '『'], ['’', '』'], ];
function toggleQuotesInAnElement(el) { let text = el.innerHTML; for (const pair of charPairs) { if (squareQuotes) { text = text.replaceAll(pair[0], pair[1]); } else { text = text.replaceAll(pair[1], pair[0]); } } el.innerHTML = text; }
|
正如注释所讲的,应使用 innerHTML 而不是 innerText,否则可能会破坏该元素的原有结构(如里面的链接、脚注等)。
再然后,实现切换页面上所有合适元素内的引号的函数。在一开始,需要先反转 squareQuotes 的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
function toggleQuotes() { squareQuotes = !squareQuotes; document .querySelectorAll( 'div#site-subtitle, a.article-title, div.content, ' + 'span.site-name, h1.post-title, ol.toc, article p, article li, ' + 'article h1, article h2, article h3, article h4, article h5, article h6', ) .forEach(toggleQuotesInAnElement); }
|
这里我的选择器是适配的 Hexo Butterfly,你可以参考以下列表去适配你自己的选择器:
- 主页:
div#site-subtitle:大副标题
a.article-title:文章标题
div.content:文章摘要
- 文章页面:
span.site-name:最上面导航栏里显示的文章标题
h1.post-title:文章大标题
ol.toc:右侧目录
article p, article li:文章内的正文
article h1 - article h6:文章内的 1 到 6 级标题(这样写的考虑见上面注释)
- 代码块里就算了
Butterfly 的右下角有一排功能按钮,我选择把开关加到那里:
1 2 3 4 5 6 7
| const btn = document.createElement('button'); btn.type = 'button'; btn.title = '“弯引号”和「直角引号」切换'; btn.innerHTML = '<i class="fas fa-quote-left"></i>'; btn.addEventListener('click', toggleQuotes); document.getElementById('rightside-config-show').appendChild(btn);
|
这里,我不直接给 div#rightside-config-show 加 innerHTML,用 onclick="toggleQuotes()",是考虑到下面的 IIFE。
用 localStorage 记住用户偏好
对于喜欢直角引号的用户,这样写有一个问题,那就是每次进一个页面都必须手动开一下,挺烦的。这里,我们使用 localStorage 记住用户偏好。
在一开始给 squareQuotes 赋值处,改为:
1
| let squareQuotes = localStorage.getItem('squareQuotes') === 'true';
|
由于 localStorage 只能存储字符串,所以这里我们选择存储字符串形式的 'true' 和 'false'。在一开始,用户浏览器里是没有这个值的,getItem 会返回 null,所以这里用 === 'true' 判断,一举两得。
在 toggleQuotes 每次反转状态后,我们也要把反转后的状态存起来,即在 squareQuotes = !squareQuotes; 后添加:
1
| localStorage.setItem('squareQuotes', squareQuotes.toString());
|
在每次页面的一开始,我们需要立即应用用户的偏好:
1 2 3
| squareQuotes = !squareQuotes; toggleQuotes();
|
IIFE
把代码放在立即调用函数表达式(IIFE)里是个好习惯:
不要像我一样,把全局变量弄得到处都是,最后一头撞在一起。
最终完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| (function () {
let squareQuotes = localStorage.getItem('squareQuotes') === 'true';
const charPairs = [ ['“', '「'], ['”', '」'], ['‘', '『'], ['’', '』'], ];
function toggleQuotesInAnElement(el) { let text = el.innerHTML; for (const pair of charPairs) { if (squareQuotes) { text = text.replaceAll(pair[0], pair[1]); } else { text = text.replaceAll(pair[1], pair[0]); } } el.innerHTML = text; }
function toggleQuotes() { squareQuotes = !squareQuotes; localStorage.setItem('squareQuotes', squareQuotes.toString()); document .querySelectorAll( 'div#site-subtitle, a.article-title, div.content, ' + 'span.site-name, h1.post-title, ol.toc, article p, article li, ' + 'article h1, article h2, article h3, article h4, article h5, article h6', ) .forEach(toggleQuotesInAnElement); }
const btn = document.createElement('button'); btn.type = 'button'; btn.title = '“弯引号”和「直角引号」切换'; btn.innerHTML = '<i class="fas fa-quote-left"></i>'; btn.addEventListener('click', toggleQuotes); document.getElementById('rightside-config-show').appendChild(btn);
squareQuotes = !squareQuotes; toggleQuotes(); })();
|