在英文和其他文字之间,添加一个空格,让排版变得更舒适,也更利于阅读,
// ==UserScript== // @name 我帮你加上空格嗷~ // @namespace Maybe you forgot a space. // @match *://*/* // @grant none // @author 稻米鼠 // @version 0.0.5 // @author - // @description 在英文和其他文字之间,添加一个空格,让排版变得更舒适,也更利于阅读, // ==/UserScript== window.addEventListener('load', async function(){ /** * observer 配置 * @property { boolean } childList - 观察目标子节点的变化,比如添加或者删除目标子节点,不包括修改子节点以及子节点后代的变化 * @property { boolean } subtree - 目标以及目标的后代改变都会观察 * @property { boolean } attributes - 观察目标属性的改变 * @property { boolean } characterData - 观察目标数据的改变 * @property { boolean } attributeOldValue - 默认为 true,表示需要记录改变前的目标属性值,设置了 attributeOldValue 可以省略 attributes 设置 * @property { boolean } characterDataOldValue - 默认为 true,表示需要记录改变前的目标数据值,设置了 characterDataOldValue 可以省略 characterData 设置 * @property { array } attributeFilter - 如果不是所有的属性改变都需要被观察,并且 attributes 设置为 true 或者被忽略,那么设置一个需要观察的属性本地名称(不需要命名空间)的列表 */ const config = { attributeOldValue: false, characterData: true, childList: true, subtree: true, }; /** * 遍历元素的全部祖先元素,判断是否应该对此元素进行进一步处理 * @param {*} el 要测试段元素 */ const nodeTester = el=>{ if(/^body$/i.test(el.tagName)){ return true }else if( /^(style|script|pre|code)$/i.test(el.tagName) || /^true$/i.test(el.contentEditable) ){ return false }else{ return nodeTester(el.parentNode) } } /** * 递归处理元素和子元素 * * @param { object } el 页面元素 * @returns */ const addSpace = async el=>{ // 如果 这是一个文本节点,则对内容进行替换 // [英文 和 标点]与其他内容之间,加上空格 if( el.nodeType === Node.TEXT_NODE ){ el.textContent = el.textContent .replace(/([^\s\w\p{P}])([\w\p{P}]+)/gu, (match, p1, p2) => /[a-zA-Z0-9]/.test(p2) ? p1 + ' ' + p2 : match ) .replace(/([\w\p{P}]+)([^\s\w\p{P}])/gu, (match, p1, p2) => /[a-zA-Z0-9]/.test(p1) ? p1 + ' ' + p2 : match ); }else if( el.nodeType === Node.ELEMENT_NODE && nodeTester(el) ){ // 如果这是一个元素节点,并且不是特殊元素,则对它的子元素进行遍历 for await (e of el.childNodes){ await addSpace(e) } } return Promise.resolve() } // 页面加载完成后处理整个页面 await addSpace(document.body) // 声明用以监控页面的 observer 对象 let observer /** * 当页面发生变化时,处理记录元素变化的数组 * * @param { array } record MutationRecord * @returns */ const pageChangeWatcher = async record =>{ // 先停止对页面变化的监控,避免陷入死循环 observer.disconnect() // 发生变化的元素合集 const els = record // 改变的类型为 characterData,并且不是 body 元素的话 .filter( (e) => /^characterData$/i.test(e.type) && !/^body$/i.test(e.target.tagName) ) .map((e) => e.target) // 把发生改变的元素放入合集 // 改变的类型为 childList,则把新增的元素放入合集 record.filter( e=>/^childList$/i.test(e.type)).forEach(e=>{ e.addedNodes.forEach(node=>els.push(node)) }) // 遍历合集中所有元素 for await (e of els){ if(nodeTester(e)) await addSpace(e) } // 页面处理完成之后重新监控页面变化 if(observer) observer.observe(document.body, config); } // 监控页面变化 observer = new MutationObserver(pageChangeWatcher); observer.observe(document.body, config); })