您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
用于给B站好友添加备注名,可导入导出
// ==UserScript== // @name B站粉丝、关注列表备注名功能 // @namespace http://nsfjgf.cn/ // @version 0.1 // @description 用于给B站好友添加备注名,可导入导出 // @author nsfjgf // @run-at document-end // @grant none // @match https://space.bilibili.com/* // @license GPL-3.0 // ==/UserScript== (function () { "use strict"; let name_arr = []; if (localStorage.getItem("name_arr")) { name_arr = JSON.parse(localStorage.getItem("name_arr")); } function createBtn(params) { let fix_dom = document.createElement("div"); fix_dom.classList.add("my__fix"); fix_dom.innerHTML = '<div class="export">导出</div><div class="import"><input id="my_export" type="file" accept=".json"><label for="my_export">导入</label></div>'; document.body.appendChild(fix_dom); const dragBox = document.querySelector(".my__fix"); let isDragging = false; let offsetY = 0; dragBox.addEventListener("mousedown", (e) => { isDragging = true; offsetY = e.clientY - dragBox.offsetTop; dragBox.style.transition = "none"; // 取消过渡动画以避免延迟 }); document.addEventListener("mousemove", (e) => { if (isDragging) { dragBox.style.top = e.clientY - offsetY + "px"; } }); document.addEventListener("mouseup", () => { isDragging = false; }); } createBtn(); const exportFileJSON = (data = {}, filename = "dataJSON.json") => { if (typeof data === "object") { data = JSON.stringify(data, null, 4); } // 导出数据 const blob = new Blob([data], { type: "text/json" }), e = new MouseEvent("click"), a = document.createElement("a"); a.download = filename; a.href = window.URL.createObjectURL(blob); a.dataset.downloadurl = ["text/json", a.download, a.href].join(":"); a.dispatchEvent(e); }; function addExportListener() { $(".export").addEventListener("click", () => { exportFileJSON(name_arr, "name_arr.json"); }); } addExportListener(); const importFileJSON = (ev) => { return new Promise((resolve, reject) => { const fileDom = ev.target, file = fileDom.files[0]; // 格式判断 if (file.type !== "application/json") { reject("仅允许上传json文件"); } // 检验是否支持FileRender if (typeof FileReader === "undefined") { reject("当前浏览器不支持FileReader"); } // 执行后清空input的值,防止下次选择同一个文件不会触发onchange事件 ev.target.value = ""; // 执行读取json数据操作 const reader = new FileReader(); reader.readAsText(file); // 读取的结果还有其他读取方式,我认为text最为方便 reader.onerror = (err) => { reject("json文件解析失败", err); }; reader.onload = () => { const resultData = reader.result; if (resultData) { try { const importData = JSON.parse(resultData); resolve(importData); } catch (error) { reject("读取数据解析失败", error); } } else { reject("读取数据解析失败", error); } }; }); }; function mergeName_arr(data) { data.forEach((item) => { if (name_arr.find((item2) => item2.id === item.id)) return; name_arr.push(item); }); localStorage.setItem("name_arr", JSON.stringify(name_arr)); alert("导入合并成功,刷新后生效"); } function addImportListenr() { $("my_export").addEventListener("change", (event) => { // exportFileJSON(name_arr, "name_arr.json"); importFileJSON(event) .then((res) => { // 回显数据 // showImportData(res); mergeName_arr(res); }) .catch((err) => { alert(err); }); }); } addImportListenr(); function addDynamicStyles() { // 检查是否已经添加过,避免重复添加 const style = document.createElement("style"); style.id = "dynamic-input-styles"; style.textContent = ` /* 基础输入框样式 */ .edit_input { color: #999999; font-size:14px; line-height:20px; outline:none; width: 200px; margin:4px 0 4px 4px; text-indent:4px; } /* 焦点状态 */ .edit_input:focus { border-color: #2196F3; border-radius:4px; box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2); } .my__fix { position: fixed; top: 50%; left: 0; background: #fff; border: 1px solid #ccc; cursor: pointer; padding: 6px; z-index:99999999; } .my__fix:hover { background: #f5f5f5; } .my__fix .import{ margin-top:2px; } #my_export{ width: 0; height: 0; } `; document.head.appendChild(style); } addDynamicStyles(); function $(select) { if (select.includes(".")) return document.querySelector(select); return document.getElementById(select); } function updateUserName(id, name) { let index = name_arr.findIndex((item) => item.id === id); name = name.replace(/[\r\n]/g, ""); if (name != "") { if (index !== -1) { name_arr[index].name = name; } else { name_arr.push({ id, name }); } } else { name_arr.splice(index, 1); } localStorage.setItem("name_arr", JSON.stringify(name_arr)); } // 创建一个新的 <div> 元素 function init() { let listBox = document.querySelector(".relation-content"); if (listBox.querySelector(".edit_input")) return; let userList = listBox.querySelectorAll(".item"); // window.hasCall = true; for (let i = 0; i < userList.length; i++) { let userid = userList[i] .querySelector("[data-user-profile-id]") .getAttribute("data-user-profile-id"); let nameDom = userList[i].querySelector(".relation-card-info__uname div"); nameDom.style.webkitLineClamp = 3; let div = document.createElement("div"); div.setAttribute("contenteditable", "plaintext-only"); div.innerText = name_arr.find((item) => item.id === userid) ? name_arr.find((item) => item.id === userid).name : "备注名"; div.classList.add("edit_input"); div.addEventListener("click", (e) => { e.stopPropagation(); e.preventDefault(); }); div.addEventListener("focusout", (e) => { let name = e.target.innerText; updateUserName(userid, name); }); nameDom.appendChild(div); } } function callInit() { let listBox = document.querySelector(".relation-content"); if (!!listBox) { let userList = listBox.querySelectorAll(".item"); if (userList.length > 0) { initAndClick(); return; } else { setTimeout(() => { callInit(); }, 200); } } else { setTimeout(() => { callInit(); }, 200); } } callInit(); //这样子调用没用 function throttle(fn, delay) { let timer = null; return function () { if (timer) { return; } timer = setTimeout(() => { fn.apply(this); timer = null; }, delay); }; } let initThrottle = throttle(init, 200); const observer1 = new MutationObserver((mutationsList, obs) => { for (let mutation of mutationsList) { // console.log("这里执行吧0000", mutation); if (mutation.type === "childList" && mutation.removedNodes.length > 0) { initThrottle(); } else if (mutation.type === "attributes") { console.log( "The " + mutation.attributeName + " attribute was modified." ); } } }); // 保存原生方法 const nativePushState = history.pushState; const nativeReplaceState = history.replaceState; // 重写 pushState history.pushState = function (state, title, url) { nativePushState.apply(this, arguments); dispatchUrlChangeEvent(url); // 触发自定义事件 }; // 重写 replaceState history.replaceState = function (state, title, url) { nativeReplaceState.apply(this, arguments); dispatchUrlChangeEvent(url); }; // 创建自定义事件 function dispatchUrlChangeEvent(url) { const event = new CustomEvent("urlchange", { detail: { url }, }); window.dispatchEvent(event); } let self_url = ""; function initAndClick() { init(); const targetNode1 = document.querySelector(".relation-content"); observer1.observe(targetNode1, { childList: true, subtree: true }); // 监听自定义事件 window.addEventListener("urlchange", (e) => { if (self_url !== e.detail.url) { setTimeout(() => { if (self_url !== "") { observer1.disconnect(); } const targetNode = document.querySelector(".relation-content"); observer1.observe(targetNode, { childList: true, subtree: true }); }, 200); } self_url = e.detail.url; }); } })();