Greasy Fork is available in English.

IThome Pro

优化ithome网页端浏览效果

  1. // ==UserScript==
  2. // @name IThome Pro
  3. // @version 4.6
  4. // @description 优化ithome网页端浏览效果
  5. // @match *://*.ithome.com/*
  6. // @run-at document-start
  7. // @namespace https://greasyfork.org/users/1354671
  8. // ==/UserScript==
  9.  
  10. (function () {
  11. "use strict";
  12.  
  13. // 启用评论框:true:启用 false:关闭
  14. const showCommentBox = false;
  15.  
  16. // 定义样式-hideStyle:不透明度 0
  17. const hideStyle = document.createElement("style");
  18. hideStyle.innerHTML = `body { opacity: 0; }`;
  19. document.head.appendChild(hideStyle);
  20.  
  21. // 跳转到 blog 页面,加载完成前隐藏原始页面
  22. if (
  23. window.location.href === "https://www.ithome.com" ||
  24. window.location.href === "https://www.ithome.com/"
  25. ) {
  26. document.head.appendChild(hideStyle);
  27. window.location.replace("https://www.ithome.com/blog/");
  28. return;
  29. }
  30.  
  31. // blog 页面加载完成前隐藏原始页面
  32. if (window.location.href.startsWith("https://www.ithome.com/blog/")) {
  33. document.head.appendChild(hideStyle);
  34. }
  35.  
  36. // 函数:保持页面激活,这样可以去除弹出的登录框
  37. function keepPageActive() {
  38. const event = new Event("mousemove", { bubbles: true, cancelable: true });
  39.  
  40. // 设置定时器,每0.1秒触发一次事件
  41. const intervalId = setInterval(() => {
  42. document.dispatchEvent(event);
  43. }, 100); // 0.1秒(100毫秒)
  44.  
  45. // 5秒后停止定时器
  46. setTimeout(() => {
  47. clearInterval(intervalId);
  48. console.log("Stopped keeping page active.");
  49. }, 5000); // 5秒(5000毫秒)
  50. }
  51.  
  52. // [调用] 保持页面激活
  53. keepPageActive();
  54.  
  55. // 函数:净化页面 利用 AdGuard 规则
  56. function hideElements() {
  57. const selectors = [
  58. ...(!showCommentBox ? ["#postcomment3"] : []),
  59. "#nav",
  60. "#top",
  61. "#tt",
  62. "#list > div.fr.fx:last-child",
  63. "#side_func",
  64. "#dt > div.fl.content:first-child > div.cv:first-child",
  65. "#dt > div.fr.fx:last-child",
  66. "#dt > div.fl.content:first-child > div.newsgrade:nth-child(6)",
  67. "#dt > div.fl.content:first-child > div.shareto:nth-child(7)",
  68. "#dt > div.fl.content:first-child > iframe.dajia:nth-child(10)",
  69. "#dt > div.fl.content:first-child > div.newsgrade:nth-child(8)",
  70. "#dt > div.fl.content:first-child > div.newserror:nth-child(7)",
  71. "#dt > div.fl.content:first-child > div.newsreward:nth-child(6)",
  72. "#dt > div.fl.content:first-child > div.shareto:nth-child(9)",
  73. "#rm-login-modal > div.modal.has-title.loaded",
  74. "#dt > div.fl.content:first-child > div.related_post:nth-child(8)",
  75. "#dt > div.fl.content:first-child > div.newserror:nth-child(5)",
  76. "#paragraph > p.ad-tips:last-child",
  77. "#fls",
  78. "#fi",
  79. "#lns",
  80. "#paragraph > div.tougao-user:nth-child(2)",
  81. "#login-guide-box",
  82. ".dajia",
  83. "#paragraph > div.tagging1:last-child",
  84. "#paragraph > p.ad-tips",
  85. '[id^="ad-id-"]',
  86. "div.-hongbao-container.bb:nth-child(6)",
  87. ];
  88.  
  89. selectors.forEach((selector) => {
  90. document.querySelectorAll(selector).forEach((element) => {
  91. element.style.display = "none";
  92. });
  93. });
  94. }
  95.  
  96. // 函数:图片处理 - 圆角、边框
  97. function processImage(image) {
  98. // 这部分匹配到的图片不处理
  99. if (image.closest("#post_comm")) return;
  100. if (image.classList.contains("titleLogo")) return;
  101. if (image.classList.contains("lazy") && image.classList.contains("emoji"))
  102. return;
  103. if (
  104. image.classList.contains("ruanmei-emoji") &&
  105. image.classList.contains("emoji")
  106. )
  107. return;
  108. if (image.id === "image-viewer" || image.classList.contains("zoomed"))
  109. return;
  110. if (image.classList.contains("comment-image")) return;
  111.  
  112. // 首页图片
  113. if (image.closest("a.img")) {
  114. const anchor = image.closest("a.img");
  115. if (!anchor.classList.contains("processed")) {
  116. anchor.style.border = "3px solid #CCC";
  117. anchor.style.borderRadius = "12px";
  118. anchor.style.display = "inline-block";
  119. anchor.style.overflow = "hidden";
  120. anchor.classList.add("processed");
  121. }
  122. // 视频预览图
  123. } else if (image.closest(".ithome_super_player")) {
  124. const videoPlayer = image.closest(".ithome_super_player");
  125. if (!videoPlayer.parentNode.classList.contains("processed")) {
  126. const wrapper = document.createElement("div");
  127. wrapper.style.border = "3px solid #CCC";
  128. wrapper.style.borderRadius = "12px";
  129. wrapper.style.overflow = "hidden";
  130. wrapper.style.maxWidth = "100%";
  131. wrapper.style.display = "block";
  132. wrapper.style.margin = "0 auto";
  133. wrapper.classList.add("processed");
  134. videoPlayer.parentNode.insertBefore(wrapper, videoPlayer);
  135. wrapper.appendChild(videoPlayer);
  136.  
  137. // 视频预览图根据父元素高度调整
  138. const img = videoPlayer.querySelector("img");
  139. if (img) {
  140. const imgWidth = img.getAttribute("w");
  141. const imgHeight = img.getAttribute("h");
  142. const parentHeight = wrapper.offsetHeight;
  143.  
  144. if (imgWidth > wrapper.offsetWidth) {
  145. const aspectRatio = imgWidth / imgHeight;
  146. img.style.height = `${parentHeight}px`;
  147. img.style.width = `${parentHeight * aspectRatio}px`;
  148. img.style.objectFit = "cover";
  149. } else {
  150. img.style.width = `${imgWidth}px`;
  151. img.style.height = `${imgHeight}px`;
  152. }
  153. }
  154. }
  155. } else {
  156. // 超长图片宽度 450px
  157. if (image.height > 1000) {
  158. image.style.borderRadius = "12px";
  159. image.style.border = "3px solid #CCC";
  160. image.style.width = "400px";
  161. image.style.maxWidth = "400px";
  162. image.style.height = "auto";
  163. image.style.objectFit = "cover";
  164. image.style.overflow = "hidden";
  165. // 常规图片宽度 450px
  166. } else {
  167. image.style.borderRadius = "12px";
  168. image.style.border = "3px solid #CCC";
  169. image.style.maxWidth = "450px";
  170. }
  171. }
  172. }
  173.  
  174. // [调用] 图片处理
  175. function setRoundedImages() {
  176. document.querySelectorAll("img").forEach((image) => processImage(image));
  177. }
  178.  
  179. // 函数:头像处理
  180. function styleHeaderImage() {
  181. const headerImages = document.querySelectorAll(".list .entry .headerimage");
  182.  
  183. headerImages.forEach((image) => {
  184. image.style.borderRadius = "12px";
  185. image.style.border = "3px solid #CCC";
  186. });
  187. }
  188.  
  189. // 函数:多图连续排列时插入间隔
  190. function wrapImagesInP() {
  191. if (window.location.href.startsWith("https://www.ithome.com/blog/")) return;
  192. document.querySelectorAll("img").forEach((image) => {
  193. // 这部分匹配到的图片不处理
  194. if (image.closest("#post_comm")) return;
  195. if (image.closest(".ithome_super_player")) return;
  196. if (
  197. image.classList.contains("ruanmei-emoji") &&
  198. image.classList.contains("emoji")
  199. )
  200. return;
  201. if (image.classList.contains("ithome_super_player")) return;
  202. if (
  203. image.parentNode.tagName.toLowerCase() === "p" &&
  204. image.parentNode.children.length === 1
  205. )
  206. return;
  207. if (image.width < 25 || image.height < 20) return;
  208.  
  209. const p = document.createElement("p");
  210. p.style.textAlign = "center";
  211. p.style.margin = "0";
  212. p.setAttribute("data-vmark", "f5e8");
  213. image.parentNode.insertBefore(p, image);
  214. p.appendChild(image);
  215. });
  216. }
  217.  
  218. // 函数:视频处理 - 圆角、边框
  219. function processIframes() {
  220. const iframes = document.querySelectorAll(
  221. '.content .post_content iframe.ithome_video, .content .post_content iframe[src*="player.bilibili.com"]',
  222. );
  223.  
  224. iframes.forEach((iframe) => {
  225. if (!iframe.classList.contains("processed")) {
  226. iframe.style.borderRadius = "12px";
  227. iframe.style.border = "3px solid #CCC";
  228. iframe.style.display = "block";
  229. iframe.style.margin = "0 auto";
  230. iframe.style.overflow = "hidden";
  231. iframe.classList.add("processed");
  232. }
  233. });
  234. }
  235.  
  236. // 函数:页面样式圆角
  237. function setRounded() {
  238. const roundeds = document.querySelectorAll(
  239. ".comm_list ul.list li.entry ul.reply, .content .post_content blockquote, " +
  240. ".add_comm input#btnComment, .card, span.card",
  241. );
  242. roundeds.forEach((rounded) => (rounded.style.borderRadius = "12px"));
  243.  
  244. document.querySelectorAll(".add_comm").forEach((addCommElement) => {
  245. addCommElement.style.borderRadius = "0px 0px 12px 12px";
  246. });
  247.  
  248. document.querySelectorAll(".card, span.card").forEach((card) => {
  249. card.style.borderRadius = "12px";
  250. card.style.transform = "scale(0.8)";
  251. });
  252. }
  253.  
  254. // 函数:移除首页信息流广告
  255. function removeAds() {
  256. document
  257. .querySelectorAll("div.bb.clearfix > div.fl > ul.bl > li")
  258. .forEach((element) => {
  259. if (element.querySelector("div.c > div.m:empty")) element.remove();
  260. });
  261. }
  262.  
  263. // 函数:自动点击「加载更多」按钮
  264. function autoClickLoadMore() {
  265. const loadMoreButton = document.querySelector("a.more");
  266.  
  267. if (loadMoreButton) {
  268. const observer = new IntersectionObserver((entries) => {
  269. entries.forEach((entry) => {
  270. if (entry.isIntersecting) {
  271. loadMoreButton.click();
  272. }
  273. });
  274. });
  275.  
  276. observer.observe(loadMoreButton);
  277.  
  278. // 监听DOM变化
  279. const mutationObserver = new MutationObserver(() => {
  280. const newLoadMoreButton = document.querySelector("a.more");
  281. if (newLoadMoreButton && !observer.observe(newLoadMoreButton)) {
  282. observer.observe(newLoadMoreButton);
  283. }
  284. });
  285.  
  286. mutationObserver.observe(document.body, {
  287. childList: true,
  288. subtree: true,
  289. });
  290. }
  291. }
  292.  
  293. // 函数:评论加载
  294. function forceLoadComments() {
  295. const footer = document.querySelector("#post_comm");
  296.  
  297. const spacer = document.createElement("div");
  298. spacer.style.height = "100vh";
  299. spacer.style.visibility = "hidden";
  300. document.body.appendChild(spacer);
  301.  
  302. window.scrollTo(0, document.body.scrollHeight);
  303.  
  304. spacer.remove();
  305. window.scrollTo(0, 0);
  306. }
  307.  
  308. // 函数:首页卡片样式
  309. function initializePage() {
  310. function makeListItemsClickable() {
  311. const listItems = document.querySelectorAll(".bl > li");
  312.  
  313. listItems.forEach((li) => {
  314. const wrapper = document.createElement("div");
  315. wrapper.classList.add("hover-wrapper");
  316. wrapper.style.position = "relative";
  317. wrapper.style.padding = "12px 16px";
  318. wrapper.style.borderRadius = "12px";
  319. wrapper.style.overflow = "hidden";
  320. wrapper.style.margin = "16px 0";
  321.  
  322. li.parentNode.insertBefore(wrapper, li);
  323. wrapper.appendChild(li);
  324.  
  325. const titleLink = li.querySelector("h2 a");
  326.  
  327. if (titleLink) {
  328. const titleText = document.createTextNode(titleLink.textContent);
  329. titleLink.replaceWith(titleText);
  330.  
  331. wrapper.style.cursor = "pointer";
  332. wrapper.addEventListener("click", () => {
  333. window.open(titleLink.href, titleLink.target || "_self");
  334. });
  335.  
  336. wrapper.addEventListener("mouseover", () => {
  337. wrapper.style.boxShadow = "0px 6px 15px rgba(0, 0, 0, 0.2)";
  338. wrapper.style.backgroundColor = getBackgroundColor();
  339. });
  340.  
  341. wrapper.addEventListener("mouseout", () => {
  342. wrapper.style.boxShadow = "none";
  343. wrapper.style.backgroundColor = "transparent";
  344. });
  345. }
  346. });
  347. }
  348.  
  349. function setHome() {
  350. const divs = document.querySelectorAll("div.fl");
  351. divs.forEach((div) => {
  352. div.style.width = "870px";
  353. });
  354. }
  355.  
  356. function removeMarginTop() {
  357. const hoverWrappers = document.querySelectorAll(".hover-wrapper");
  358. hoverWrappers.forEach((hoverWrapper) => {
  359. const listItems = hoverWrapper.querySelectorAll("li");
  360. listItems.forEach((item) => {
  361. item.style.marginTop = "0";
  362. });
  363. });
  364. }
  365.  
  366. function setDivWidthTo590() {
  367. const divs = document.querySelectorAll("div.c");
  368. divs.forEach((div) => {
  369. div.style.width = "640px";
  370. });
  371. }
  372.  
  373. function getBackgroundColor() {
  374. if (
  375. window.matchMedia &&
  376. window.matchMedia("(prefers-color-scheme: dark)").matches
  377. ) {
  378. return "#333333";
  379. } else {
  380. return "#f9f9f9";
  381. }
  382. }
  383.  
  384. makeListItemsClickable();
  385. setHome();
  386. removeMarginTop();
  387. setDivWidthTo590();
  388. }
  389.  
  390. // 函数:评论区图片放大
  391. function replaceImageWrapper() {
  392. const imageWrappers = document.querySelectorAll(
  393. ".post-img-list a.img-wrapper",
  394. );
  395.  
  396. imageWrappers.forEach((wrapper) => {
  397. const img = wrapper.querySelector("img");
  398. if (img) {
  399. const parent = wrapper.parentElement;
  400.  
  401. wrapper.classList.remove("img-wrapper");
  402. wrapper.classList.add("img-click");
  403.  
  404. wrapper.removeAttribute("href");
  405.  
  406. img.style.width = "30%";
  407. img.style.height = "auto";
  408. img.style.borderRadius = "12px";
  409. img.style.border = "3px solid #CCC";
  410.  
  411. let isZoomed = false;
  412.  
  413. img.addEventListener("click", () => {
  414. if (isZoomed) {
  415. img.style.width = "30%";
  416. } else {
  417. img.style.width = "100%";
  418. }
  419. img.style.height = "auto";
  420. isZoomed = !isZoomed;
  421. });
  422. }
  423. });
  424. }
  425.  
  426. // 函数:观察DOM变化,处理新刷出的内容
  427. function observeDOM() {
  428. const observer = new MutationObserver((mutationsList) => {
  429. for (const mutation of mutationsList) {
  430. if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
  431. wrapImagesInP();
  432. setRounded();
  433. removeAds();
  434. hideElements();
  435. setRoundedImages();
  436. styleHeaderImage();
  437. initializePage();
  438. replaceImageWrapper();
  439. }
  440. }
  441. });
  442.  
  443. observer.observe(document.body, { childList: true, subtree: true });
  444. }
  445.  
  446. // 监听事件
  447. window.addEventListener("scroll", autoClickLoadMore);
  448. window.addEventListener("load", function () {
  449. hideElements();
  450. forceLoadComments();
  451. removeAds();
  452. wrapImagesInP();
  453. setRounded();
  454. processIframes();
  455. setRoundedImages();
  456. styleHeaderImage();
  457. initializePage();
  458. replaceImageWrapper();
  459. observeDOM();
  460. document.body.style.opacity = "1";
  461.  
  462. // 处理图片懒加载
  463. document.querySelectorAll("img").forEach((img) => {
  464. if (img.hasAttribute("loading")) {
  465. img.removeAttribute("loading");
  466. }
  467. if (img.classList.contains("lazy")) {
  468. img.classList.remove("lazy");
  469. }
  470. if (img.dataset.src) {
  471. img.src = img.dataset.src;
  472. }
  473. if (img.dataset.original) {
  474. img.src = img.dataset.original;
  475. }
  476. img.loading = "eager";
  477. });
  478. });
  479. })();