自动在中英文之间添加空格
// ==UserScript==
// @name 中英混用加空格
// @namespace https://greasyfork.org/users/1171320
// @version 1.02
// @description 自动在中英文之间添加空格
// @author yzcjd
// @author2 Lama AI 辅助
// @match *://*/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 判断节点是否在输入框或可编辑区域内
function isEditable(node) {
if (!node) return false;
if (node.nodeType === Node.ELEMENT_NODE) {
const tag = node.tagName.toLowerCase();
if (tag === 'input' || tag === 'textarea') return true;
if (node.isContentEditable) return true;
}
// 向上找父节点,避免在输入框内部的子节点也被处理
if (node.parentElement) return isEditable(node.parentElement);
return false;
}
// 防止重复处理,给文本节点添加自定义属性标记
function markProcessed(node) {
node._ch_en_space_processed = true;
}
function isProcessed(node) {
return node._ch_en_space_processed === true;
}
// 处理文本节点加空格
function addSpaceBetweenChineseAndEnglish(node) {
if (node.nodeType === Node.TEXT_NODE) {
if (isProcessed(node)) return; // 已处理过
if (isEditable(node)) return; // 在输入框或可编辑区,跳过
// 添加空格
const oldText = node.textContent;
const newText = oldText.replace(/([\u4e00-\u9fa5])([a-zA-Z0-9_])/g, '$1 $2')
.replace(/([a-zA-Z0-9_])([\u4e00-\u9fa5])/g, '$1 $2');
if (newText !== oldText) {
node.textContent = newText;
}
markProcessed(node);
} else if (node.nodeType === Node.ELEMENT_NODE) {
for (const child of node.childNodes) {
addSpaceBetweenChineseAndEnglish(child);
}
}
}
// 防抖函数,减少频繁执行
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
}
}
// 初始化和观察 DOM 变化
function init() {
const body = document.body;
if (!body) return;
addSpaceBetweenChineseAndEnglish(body);
const observer = new MutationObserver(debounce(mutations => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (!isEditable(node)) {
addSpaceBetweenChineseAndEnglish(node);
}
});
} else if (mutation.type === 'characterData') {
const node = mutation.target;
if (!isEditable(node)) {
addSpaceBetweenChineseAndEnglish(node);
}
}
}
}, 2000)); // 2000ms节流,避免频繁执行
observer.observe(body, {
childList: true,
subtree: true,
characterData: true,
});
}
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();