笔记列表跟随当前章节滚动
目前為
// ==UserScript==
// @name 微信读书笔记列表跟随当前章节滚动
// @namespace http://tampermonkey.net/
// @version 0.6.1
// @description 笔记列表跟随当前章节滚动
// @author XQH
// @match https://weread.qq.com/web/reader/*
// @icon https://weread.qq.com/favicon.ico
// @grant none
// @license MIT
// ==/UserScript==
(function () {
"use strict";
let lastNote = null;
// 主函数,初始化脚本
function init() {
injectCss();
injectNoteButton();
addUnderlineClickListeners();
observeDOMChanges();
}
// 注入自定义CSS样式
function injectCss() {
let css = document.createElement("style");
css.type = "text/css";
css.innerHTML = `
.wr_btn.wr_btn_Big.rbb_addShelf,
.readerFooter_button.blue,
.reader_toolbar_color_container,
.toolbarItem.underlineHandWrite,
.toolbarItem.underlineStraight,
.toolbarItem.review,
.toolbarItem.query,
.wr_reader_note_panel_header_wrapper,
.toast.toast_Show {
display: none !important;
}
.readerNotePanel {
overflow-y: auto;
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 80%;
margin-left: 0;
display: none;
z-index: 10000;
background: white;
}
.readerBottomBar {
z-index: 9999;
}
`;
document.head.appendChild(css);
}
// 在底部栏注入笔记按钮
function injectNoteButton() {
let note_btn = `
<button title="笔记" class="rbb_item wr_note">
<span class="icon"></span>
<span class="txt">笔记</span>
</button>`;
let note_btn_container = document.querySelector(".readerBottomBar_content");
if (note_btn_container) {
if (note_btn_container.querySelector(".rbb_item.wr_note")) {
return;
}
note_btn_container.insertAdjacentHTML("afterbegin", note_btn);
document
.querySelector(".rbb_item.wr_note")
.addEventListener("click", function (event) {
event.stopPropagation(); // 防止点击事件冒泡到外部
let reader_note_panel = document.querySelector(".readerNotePanel");
if (
reader_note_panel.style.display === "none" ||
reader_note_panel.style.display === ""
) {
reader_note_panel.style.display = "block";
jumpNote();
// 添加事件监听器,检测点击面板外部隐藏面板
setTimeout(() => {
document.addEventListener("click", outsideClickListener);
}, 0);
} else {
reader_note_panel.style.display = "none";
document.removeEventListener("click", outsideClickListener);
}
});
}
}
function outsideClickListener(event) {
let reader_note_panel = document.querySelector(".readerNotePanel");
let btn = document.querySelector(".rbb_item.wr_note");
if (
!reader_note_panel.contains(event.target) &&
!btn.contains(event.target)
) {
reader_note_panel.style.display = "none";
document.removeEventListener("click", outsideClickListener);
}
}
// 获取当前章节标题
function getChapterTitle() {
let chapter_title = document.querySelector(".readerTopBar_title_chapter");
if (chapter_title) {
return chapter_title.innerText.trim();
}
return "";
}
// 滚动笔记面板到当前章节
function jumpNote() {
let chapterTitle = getChapterTitle();
console.log("Chapter Title: " + chapterTitle);
// 在笔记面板中查找匹配的章节
let noteChapters = document.querySelectorAll(
".wr_reader_note_panel_chapter_title"
);
for (let i = 0; i < noteChapters.length; i++) {
if (noteChapters[i].innerText.trim() === chapterTitle) {
console.log("Found chapter in notes: " + chapterTitle);
// 滚动到笔记面板中的该章节
noteChapters[i].scrollIntoView({ block: "center" });
// 如果之前有选中的笔记,滚动到该笔记
if (lastNote) {
lastNote.scrollIntoView({ block: "center" });
}
break;
}
}
// 检查当前屏幕可见范围内是否有高亮文本
let underlines = document.getElementsByClassName("wr_underline_wrapper");
// 获取视口高度和宽度
let viewportHeight = window.innerHeight || document.documentElement.clientHeight;
let viewportWidth = window.innerWidth || document.documentElement.clientWidth;
let viewportCenterY = viewportHeight / 2;
// 筛选在视口内的高亮文本
let visibleUnderlines = [];
for (let i = 0; i < underlines.length; i++) {
let rect = underlines[i].getBoundingClientRect();
if (
rect.bottom >= 0 &&
rect.top <= viewportHeight &&
rect.right >= 0 &&
rect.left <= viewportWidth
) {
visibleUnderlines.push(underlines[i]);
}
}
if (visibleUnderlines.length > 0) {
// 找到离视口中心最近的高亮文本
let closestUnderline = visibleUnderlines.reduce((prev, curr) => {
let prevRect = prev.getBoundingClientRect();
let currRect = curr.getBoundingClientRect();
let prevDistance = Math.abs((prevRect.top + prevRect.bottom) / 2 - viewportCenterY);
let currDistance = Math.abs((currRect.top + currRect.bottom) / 2 - viewportCenterY);
return prevDistance < currDistance ? prev : curr;
});
// 模拟点击最近的高亮文本,唤出复制按钮
closestUnderline.click();
setTimeout(() => {
// 工具栏应该已显示
let copyButton = document.querySelector('.toolbarItem.wr_copy');
if (copyButton) {
// 添加标志量,表示是自定义的复制操作
window.isCustomCopy = true;
function onCopy(e) {
if (window.isCustomCopy) {
let selectionText = e.target.value;
e.preventDefault();
e.stopPropagation();
window.isCustomCopy = false;
document.removeEventListener('copy', onCopy, true);
console.log('当前视野内高亮最先内容: ' + selectionText);
// 查找并滚动到该笔记项
scrollToNoteItem(selectionText);
}
}
document.addEventListener('copy', onCopy, true);
// 模拟点击复制按钮
copyButton.click();
} else {
console.log('Copy button not found.');
}
}, 100); // 根据需要调整延迟,以确保工具栏已出现
} else {
console.log("No highlighted text in viewport.");
}
}
function scrollToNoteItem(selectionText) {
let noteItems = document.querySelectorAll('.wr_reader_note_panel_item_cell_wrapper');
let foundIndex = -1;
for (let j = 0; j < noteItems.length; j++) {
let noteTextElement = noteItems[j].querySelector('.wr_reader_note_panel_item_cell_content_text');
if (noteTextElement) {
let noteText = noteTextElement.innerText.replace(/\s/g, '');
if (selectionText === noteText) {
foundIndex = j;
break;
}
}
}
if (foundIndex >= 0) {
// 滚动到item
noteItems[foundIndex].scrollIntoView({ block: "center" });
console.log('聚焦到笔记项:' + selectionText);
} else {
console.log("Cur Note not found for text: " + selectionText);
}
}
// 为高亮文本添加事件监听器
function addUnderlineClickListeners() {
let underlines = document.getElementsByClassName("wr_underline_wrapper");
for (let i = 0; i < underlines.length; i++) {
if (underlines[i].getAttribute("data-listener-added")) {
continue;
}
underlines[i].setAttribute("data-listener-added", "true");
let clickCount = 0;
let lastClickTime = 0;
const doubleClickThreshold = 20;
underlines[i].addEventListener("click", function (e) {
const currentTime = new Date().getTime();
clickCount++;
if (clickCount === 1) {
setTimeout(function () {
if (clickCount === 1) {
// 单击:可添加显示工具栏的逻辑
} else if (clickCount === 2) {
// 检测到双击
handleDoubleClick(e.target);
}
clickCount = 0;
}, doubleClickThreshold);
}
lastClickTime = currentTime;
});
}
}
// 处理高亮文本的双击事件
function handleDoubleClick(element) {
// 模拟点击元素以选中并显示工具栏
element.click();
setTimeout(() => {
// 工具栏应该已显示
let copyButton = document.querySelector(".toolbarItem.wr_copy");
if (copyButton) {
// 添加标志量,表示是自定义的复制操作
window.isCustomCopy = true;
// toast toast_Show , hide toast
// 添加事件监听器,拦截复制事件
function onCopy(e) {
if (window.isCustomCopy) {
// const selectionText = window.getSelection().toString();
// 拦截复制事件的复制内容,从 e 中获取
let selectionText = e.target.value;
e.preventDefault();
e.stopPropagation();
// 获取选中文本
// 重置标志量
window.isCustomCopy = false;
// 移除事件监听器
document.removeEventListener("copy", onCopy, true);
// 查找并点击下一条笔记
findAndClickNextNoteItem(selectionText);
}
}
document.addEventListener("copy", onCopy, true);
// 模拟点击复制按钮
copyButton.click();
} else {
console.log("Copy button not found.");
}
}, 50); // 根据需要调整延迟,以确保工具栏已出现
}
function findAndClickNextNoteItem(selectionText) {
let noteItems = document.querySelectorAll(
".wr_reader_note_panel_item_cell_wrapper.clickable"
);
let foundIndex = -1;
for (let j = 0; j < noteItems.length; j++) {
let noteTextElement = noteItems[j].querySelector(
".wr_reader_note_panel_item_cell_content_text"
);
// 移除空格和换行符
selectionText = selectionText.replace(/\s/g, "");
if (noteTextElement) {
let noteText = noteTextElement.innerText.replace(/\s/g, "");
if (selectionText === noteText) {
foundIndex = j;
break;
}
}
}
if (foundIndex >= 0) {
let nextIndex = foundIndex + 1;
if (nextIndex >= noteItems.length) {
nextIndex = 0;
}
noteItems[nextIndex].click();
lastNote = noteItems[nextIndex];
// 滚动笔记面板到下一条笔记
noteItems[nextIndex].scrollIntoView({ block: "center" });
} else {
console.log("Note not found for text: " + selectionText);
}
}
// 观察DOM变化,添加监听器到新添加的高亮文本
function observeDOMChanges() {
let targetNode = document.querySelector(".readerContent");
if (!targetNode) {
console.log("Reader content not found for observing DOM changes.");
return;
}
let config = { childList: true, subtree: true };
let callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.addedNodes.length > 0) {
addUnderlineClickListeners();
}
}
};
let observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
// 等待页面加载必要的元素
let timer = setInterval(function () {
let reader_note_panel = document.querySelector(".readerNotePanel");
if (reader_note_panel) {
clearInterval(timer);
init();
}
}, 1000);
})();