Merge pull request #3429 from LooseLi/fix-spoiler-text

fix: spoiler隐藏文本失效
This commit is contained in:
tangly1024
2025-06-05 12:41:14 +08:00
committed by GitHub

View File

@@ -1,3 +1,12 @@
/**
* 转义正则表达式中的特殊字符
* @param {string} string 需要转义的字符串
* @returns {string} 转义后的字符串
*/
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
/**
* 将Node文本中的指定标签内容转换为带有指定类名的span
* @param regex
@@ -5,17 +14,18 @@
* @param className
*/
function convertTextToSpoilerSpan(regex, node, className) {
const wholeText = node.wholeText
// 使用 textContent 替代 wholeText 以确保类型安全
const textContent = node.textContent
let outerSpan = document.createElement('span')
const fragments = []
let lastIndex = 0
let match
while ((match = regex.exec(wholeText)) !== null) {
console.log('符合要求的文字' + wholeText)
while ((match = regex.exec(textContent)) !== null) {
console.log('符合要求的文字' + textContent)
// 添加前面未匹配的部分
if (match.index > lastIndex) {
outerSpan.appendChild(
document.createTextNode(wholeText.slice(lastIndex, match.index))
document.createTextNode(textContent.slice(lastIndex, match.index))
)
}
@@ -31,8 +41,10 @@ function convertTextToSpoilerSpan(regex, node, className) {
}
if (outerSpan.childNodes.length) {
// 添加剩余未匹配的部分
if (lastIndex < wholeText.length) {
outerSpan.appendChild(document.createTextNode(wholeText.slice(lastIndex)))
if (lastIndex < textContent.length) {
outerSpan.appendChild(
document.createTextNode(textContent.slice(lastIndex))
)
}
node.replaceWith(outerSpan)
}
@@ -48,9 +60,12 @@ function processTextNodes(root, className, spoilerTag) {
const regex = new RegExp(`${spoilerTag}(.*?)${spoilerTag}`, 'g')
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode: function (node) {
return regex.test(node.wholeText)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT
if (node.nodeType === Node.TEXT_NODE) {
return regex.test(node.textContent)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT
}
return NodeFilter.FILTER_REJECT
}
})
const waitProcessNodes = []
@@ -61,6 +76,47 @@ function processTextNodes(root, className, spoilerTag) {
for (const waitProcessNode of waitProcessNodes) {
convertTextToSpoilerSpan(regex, waitProcessNode, className)
}
// 处理跨节点的 spoiler 标记
processCrossNodeSpoilers(root, className, spoilerTag)
}
/**
* 处理跨节点的 spoiler 标记
* @param {Element} root 要处理的根元素
* @param {string} className 应用于 spoiler 内容的类名
* @param {string} spoilerTag spoiler 标记符号
*/
function processCrossNodeSpoilers(root, className, spoilerTag) {
if (root.nodeType !== Node.ELEMENT_NODE) return
const html = root.innerHTML
// 处理原始标签,如果是已经转义过的,则去除转义
let originalTag = spoilerTag
if (spoilerTag.startsWith('\\') || spoilerTag.includes('\\[')) {
originalTag = spoilerTag.replace(/\\/g, '')
}
// 创建正则表达式,直接匹配原始标签
const regex = new RegExp(`\\${originalTag}([\\s\\S]*?)\\${originalTag}`, 'g')
const hasMatch = regex.test(html)
if (!hasMatch) return
// 重置正则表达式
regex.lastIndex = 0
// 替换匹配项
const newHtml = html.replace(regex, function (match, content) {
return `<span class="${className}">${content}</span>`
})
// 如果内容有变化,更新 DOM
if (newHtml !== html) {
root.innerHTML = newHtml
}
}
/**