add _extensions
This commit is contained in:
@@ -0,0 +1,418 @@
|
||||
// clab-tooltip.js - 完全隔离事件版本(修改后的显示位置逻辑)
|
||||
(function() {
|
||||
// 全局变量存储从 RN.html 提取的内容
|
||||
let rnContentCache = {};
|
||||
let isRnContentLoaded = false;
|
||||
let activeTooltip = null;
|
||||
let hideTimer = null;
|
||||
let isMouseOverTooltip = false;
|
||||
|
||||
// 从 RN.html 提取内容(保持不变)
|
||||
async function loadRnContent() {
|
||||
try {
|
||||
console.log('加载 RN.html...');
|
||||
const response = await fetch('RN.html');
|
||||
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
|
||||
|
||||
const htmlText = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const rnDoc = parser.parseFromString(htmlText, 'text/html');
|
||||
|
||||
// 查找所有包含 data-options 的元素
|
||||
const optionElements = rnDoc.querySelectorAll('[data-options]');
|
||||
console.log(`找到 ${optionElements.length} 个 data-options 元素`);
|
||||
|
||||
optionElements.forEach(element => {
|
||||
const optionValue = element.getAttribute('data-options');
|
||||
if (!optionValue) return;
|
||||
|
||||
// 向前查找最近的 blockquote
|
||||
let prevElement = element.previousElementSibling;
|
||||
let blockquote = null;
|
||||
|
||||
// 先找前一个兄弟元素
|
||||
while (prevElement) {
|
||||
if (prevElement.tagName === 'BLOCKQUOTE') {
|
||||
blockquote = prevElement;
|
||||
break;
|
||||
}
|
||||
prevElement = prevElement.previousElementSibling;
|
||||
}
|
||||
|
||||
// 如果没找到,向上查找
|
||||
if (!blockquote) {
|
||||
let parent = element.parentElement;
|
||||
while (parent) {
|
||||
let sibling = parent.previousElementSibling;
|
||||
while (sibling) {
|
||||
if (sibling.tagName === 'BLOCKQUOTE') {
|
||||
blockquote = sibling;
|
||||
break;
|
||||
}
|
||||
sibling = sibling.previousElementSibling;
|
||||
}
|
||||
if (blockquote) break;
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockquote) {
|
||||
const lis = blockquote.querySelectorAll('li');
|
||||
if (lis.length > 0) {
|
||||
const liContents = Array.from(lis).map(li => li.textContent.trim());
|
||||
|
||||
if (!rnContentCache[optionValue]) {
|
||||
rnContentCache[optionValue] = [];
|
||||
}
|
||||
|
||||
// 存储所有引用
|
||||
rnContentCache[optionValue].push({
|
||||
contents: liContents
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
isRnContentLoaded = true;
|
||||
|
||||
console.log('内容加载完成');
|
||||
Object.keys(rnContentCache).forEach(key => {
|
||||
console.log(`"${key}": ${rnContentCache[key].length} Questions`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 tooltip(保持不变)
|
||||
function createTooltip() {
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'clab-tooltip';
|
||||
tooltip.id = 'clab-tooltip-element';
|
||||
|
||||
// 完全阻止所有事件冒泡
|
||||
const stopAllEvents = (e) => {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
// 阻止所有可能的事件
|
||||
const events = [
|
||||
'mousedown', 'mouseup', 'click', 'dblclick',
|
||||
'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave',
|
||||
'wheel', 'scroll',
|
||||
'touchstart', 'touchmove', 'touchend', 'touchcancel',
|
||||
'pointerdown', 'pointermove', 'pointerup', 'pointercancel',
|
||||
'dragstart', 'dragover', 'drop'
|
||||
];
|
||||
|
||||
events.forEach(eventType => {
|
||||
tooltip.addEventListener(eventType, stopAllEvents, true); // 捕获阶段
|
||||
tooltip.addEventListener(eventType, stopAllEvents, false); // 冒泡阶段
|
||||
});
|
||||
|
||||
// 允许 wheel 事件用于滚动,但阻止冒泡
|
||||
tooltip.addEventListener('wheel', function(e) {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
// 允许默认的滚动行为
|
||||
}, { passive: false });
|
||||
|
||||
// 允许鼠标在 tooltip 内移动
|
||||
tooltip.addEventListener('mousemove', function(e) {
|
||||
e.stopPropagation();
|
||||
}, { passive: true });
|
||||
|
||||
document.body.appendChild(tooltip);
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
// 显示 tooltip - 已修改位置逻辑
|
||||
function showTooltip(element, tooltip) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const optionValue = element.getAttribute('data-options');
|
||||
|
||||
// 清空内容
|
||||
tooltip.innerHTML = '';
|
||||
|
||||
if (rnContentCache[optionValue] && rnContentCache[optionValue].length > 0) {
|
||||
const entries = rnContentCache[optionValue];
|
||||
|
||||
// 标题显示选项
|
||||
const title = document.createElement('div');
|
||||
title.className = 'tooltip-title';
|
||||
title.textContent = optionValue;
|
||||
if (entries.length > 1) {
|
||||
title.textContent += ` (${entries.length} questions)`;
|
||||
}
|
||||
tooltip.appendChild(title);
|
||||
|
||||
// 显示所有引用
|
||||
entries.forEach((entry, entryIndex) => {
|
||||
const content = entry.contents;
|
||||
|
||||
if (content.length === 1) {
|
||||
// 单一条目
|
||||
const entryDiv = document.createElement('div');
|
||||
entryDiv.className = 'entry-single';
|
||||
|
||||
if (entries.length >= 1) {
|
||||
entryDiv.innerHTML = `<span class="q-label">Q${entryIndex + 1}:</span> ${content[0]}`;
|
||||
} else {
|
||||
entryDiv.textContent = content[0];
|
||||
}
|
||||
|
||||
tooltip.appendChild(entryDiv);
|
||||
} else {
|
||||
// 多个条目
|
||||
const entryDiv = document.createElement('div');
|
||||
entryDiv.className = 'entry-multiple';
|
||||
|
||||
if (entries.length > 1) {
|
||||
const header = document.createElement('div');
|
||||
header.className = 'q-header';
|
||||
header.textContent = `Q${entryIndex + 1}:`;
|
||||
entryDiv.appendChild(header);
|
||||
}
|
||||
|
||||
const ol = document.createElement('ol');
|
||||
ol.className = 'q-list';
|
||||
|
||||
content.forEach((item, itemIndex) => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<span class="q-sublabel">${entryIndex + 1}.${itemIndex + 1}</span> ${item}`;
|
||||
ol.appendChild(li);
|
||||
});
|
||||
|
||||
entryDiv.appendChild(ol);
|
||||
tooltip.appendChild(entryDiv);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 没有找到内容
|
||||
const noContent = document.createElement('div');
|
||||
noContent.className = 'tooltip-no-content';
|
||||
noContent.textContent = '未找到相关内容';
|
||||
tooltip.appendChild(noContent);
|
||||
}
|
||||
|
||||
// 确保 tooltip 有合适的尺寸
|
||||
tooltip.style.position = 'fixed';
|
||||
tooltip.style.zIndex = '10000';
|
||||
tooltip.style.maxWidth = '400px';
|
||||
tooltip.style.maxHeight = '500px';
|
||||
tooltip.style.overflowY = 'auto';
|
||||
tooltip.style.boxSizing = 'border-box';
|
||||
|
||||
// 首先隐藏并获取尺寸
|
||||
tooltip.style.visibility = 'hidden';
|
||||
tooltip.classList.add('active');
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
const tooltipWidth = Math.min(tooltip.scrollWidth, 400);
|
||||
const tooltipHeight = Math.min(tooltip.scrollHeight, 500);
|
||||
|
||||
tooltip.style.width = tooltipWidth + 'px';
|
||||
tooltip.style.height = 'auto';
|
||||
|
||||
const padding = 5;
|
||||
const minDistanceFromEdge = 20;
|
||||
|
||||
let left, top;
|
||||
let position = 'right'; // 首选右侧
|
||||
|
||||
// 检查右侧空间
|
||||
if (rect.right + padding + tooltipWidth <= window.innerWidth - minDistanceFromEdge) {
|
||||
// 右侧有足够空间
|
||||
left = rect.right + padding;
|
||||
top = rect.top;
|
||||
position = 'right';
|
||||
} else if (rect.left - padding - tooltipWidth >= minDistanceFromEdge) {
|
||||
// 左侧有足够空间
|
||||
left = rect.left - padding - tooltipWidth;
|
||||
top = rect.top;
|
||||
position = 'left';
|
||||
} else {
|
||||
// 两侧都没有足够空间,显示在下方
|
||||
left = Math.max(
|
||||
minDistanceFromEdge,
|
||||
Math.min(
|
||||
rect.left + (rect.width - tooltipWidth) / 2,
|
||||
window.innerWidth - tooltipWidth - minDistanceFromEdge
|
||||
)
|
||||
);
|
||||
top = rect.bottom + padding;
|
||||
position = 'bottom';
|
||||
}
|
||||
|
||||
// 垂直边界检查 - 确保不超出屏幕
|
||||
if (top + tooltipHeight > window.innerHeight - minDistanceFromEdge) {
|
||||
// 如果下方空间不足,尝试显示在上方
|
||||
if (rect.top - padding - tooltipHeight >= minDistanceFromEdge) {
|
||||
top = rect.top - padding - tooltipHeight;
|
||||
} else {
|
||||
// 上下都不够,显示在中间
|
||||
top = Math.max(
|
||||
minDistanceFromEdge,
|
||||
Math.min(
|
||||
top,
|
||||
window.innerHeight - tooltipHeight - minDistanceFromEdge
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 水平边界检查 - 确保不超出屏幕
|
||||
if (position === 'right' || position === 'left') {
|
||||
left = Math.max(
|
||||
minDistanceFromEdge,
|
||||
Math.min(left, window.innerWidth - tooltipWidth - minDistanceFromEdge)
|
||||
);
|
||||
}
|
||||
|
||||
tooltip.style.left = left + 'px';
|
||||
tooltip.style.top = top + 'px';
|
||||
tooltip.style.visibility = 'visible';
|
||||
|
||||
// 添加一个小箭头指示方向
|
||||
tooltip.setAttribute('data-position', position);
|
||||
|
||||
activeTooltip = tooltip;
|
||||
isMouseOverTooltip = true;
|
||||
|
||||
// 添加 overlay 防止 body 交互
|
||||
addOverlay();
|
||||
}
|
||||
|
||||
// 添加半透明 overlay(保持不变)
|
||||
function addOverlay() {
|
||||
// 移除现有的 overlay
|
||||
removeOverlay();
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'tooltip-overlay';
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
`;
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// 将 overlay 的 pointer-events 设为 auto,但阻止所有事件
|
||||
overlay.addEventListener('mousedown', stopEvent, true);
|
||||
overlay.addEventListener('mouseup', stopEvent, true);
|
||||
overlay.addEventListener('click', stopEvent, true);
|
||||
overlay.addEventListener('mousemove', stopEvent, true);
|
||||
overlay.addEventListener('wheel', stopEvent, true);
|
||||
|
||||
function stopEvent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 移除 overlay(保持不变)
|
||||
function removeOverlay() {
|
||||
const overlay = document.getElementById('tooltip-overlay');
|
||||
if (overlay) {
|
||||
overlay.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏 tooltip(保持不变)
|
||||
function hideTooltip() {
|
||||
if (!isMouseOverTooltip && activeTooltip) {
|
||||
activeTooltip.classList.remove('active');
|
||||
activeTooltip = null;
|
||||
removeOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
// 强制隐藏 tooltip(保持不变)
|
||||
function forceHideTooltip() {
|
||||
if (activeTooltip) {
|
||||
activeTooltip.classList.remove('active');
|
||||
activeTooltip = null;
|
||||
isMouseOverTooltip = false;
|
||||
removeOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化(保持不变)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('初始化 clab-tooltip');
|
||||
|
||||
const clabElements = document.querySelectorAll('.clab[data-options]');
|
||||
console.log(`找到 ${clabElements.length} 个 .clab 元素`);
|
||||
|
||||
if (clabElements.length === 0) return;
|
||||
|
||||
// 加载内容
|
||||
loadRnContent();
|
||||
|
||||
// 创建 tooltip
|
||||
const tooltip = createTooltip();
|
||||
|
||||
// tooltip 鼠标进入/离开
|
||||
tooltip.addEventListener('mouseenter', function() {
|
||||
isMouseOverTooltip = true;
|
||||
clearTimeout(hideTimer);
|
||||
});
|
||||
|
||||
tooltip.addEventListener('mouseleave', function() {
|
||||
isMouseOverTooltip = false;
|
||||
hideTimer = setTimeout(() => {
|
||||
forceHideTooltip();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// 为所有 .clab 元素添加事件监听
|
||||
clabElements.forEach(clab => {
|
||||
clab.addEventListener('mouseenter', function() {
|
||||
clearTimeout(hideTimer);
|
||||
|
||||
setTimeout(() => {
|
||||
if (isRnContentLoaded) {
|
||||
showTooltip(this, tooltip);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
clab.addEventListener('mouseleave', function() {
|
||||
hideTimer = setTimeout(() => {
|
||||
hideTooltip();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
// ESC 键隐藏 tooltip
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && activeTooltip) {
|
||||
forceHideTooltip();
|
||||
}
|
||||
});
|
||||
|
||||
// 点击其他地方隐藏 tooltip
|
||||
document.addEventListener('click', function(e) {
|
||||
if (activeTooltip && !activeTooltip.contains(e.target)) {
|
||||
forceHideTooltip();
|
||||
}
|
||||
});
|
||||
|
||||
// 窗口大小改变时隐藏 tooltip
|
||||
window.addEventListener('resize', forceHideTooltip);
|
||||
|
||||
// 滚动时隐藏 tooltip
|
||||
window.addEventListener('scroll', forceHideTooltip);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user