Greasy Fork is available in English.

IThome Pro

优化ithome网页端浏览效果

// ==UserScript==
// @name         IThome Pro
// @version      4.6
// @description  优化ithome网页端浏览效果
// @match        *://*.ithome.com/*
// @run-at       document-start
// @namespace    https://greasyfork.org/users/1354671
// ==/UserScript==

(function () {
  "use strict";

  // 启用评论框:true:启用 false:关闭
  const showCommentBox = false;

  // 定义样式-hideStyle:不透明度 0
  const hideStyle = document.createElement("style");
  hideStyle.innerHTML = `body { opacity: 0; }`;
  document.head.appendChild(hideStyle);

  // 跳转到 blog 页面,加载完成前隐藏原始页面
  if (
    window.location.href === "https://www.ithome.com" ||
    window.location.href === "https://www.ithome.com/"
  ) {
    document.head.appendChild(hideStyle);
    window.location.replace("https://www.ithome.com/blog/");
    return;
  }

  // blog 页面加载完成前隐藏原始页面
  if (window.location.href.startsWith("https://www.ithome.com/blog/")) {
    document.head.appendChild(hideStyle);
  }

  // 函数:保持页面激活,这样可以去除弹出的登录框
  function keepPageActive() {
    const event = new Event("mousemove", { bubbles: true, cancelable: true });

    // 设置定时器,每0.1秒触发一次事件
    const intervalId = setInterval(() => {
      document.dispatchEvent(event);
    }, 100); // 0.1秒(100毫秒)

    // 5秒后停止定时器
    setTimeout(() => {
      clearInterval(intervalId);
      console.log("Stopped keeping page active.");
    }, 5000); // 5秒(5000毫秒)
  }

  // [调用] 保持页面激活
  keepPageActive();

  // 函数:净化页面 利用 AdGuard 规则
  function hideElements() {
    const selectors = [
      ...(!showCommentBox ? ["#postcomment3"] : []),
      "#nav",
      "#top",
      "#tt",
      "#list > div.fr.fx:last-child",
      "#side_func",
      "#dt > div.fl.content:first-child > div.cv:first-child",
      "#dt > div.fr.fx:last-child",
      "#dt > div.fl.content:first-child > div.newsgrade:nth-child(6)",
      "#dt > div.fl.content:first-child > div.shareto:nth-child(7)",
      "#dt > div.fl.content:first-child > iframe.dajia:nth-child(10)",
      "#dt > div.fl.content:first-child > div.newsgrade:nth-child(8)",
      "#dt > div.fl.content:first-child > div.newserror:nth-child(7)",
      "#dt > div.fl.content:first-child > div.newsreward:nth-child(6)",
      "#dt > div.fl.content:first-child > div.shareto:nth-child(9)",
      "#rm-login-modal > div.modal.has-title.loaded",
      "#dt > div.fl.content:first-child > div.related_post:nth-child(8)",
      "#dt > div.fl.content:first-child > div.newserror:nth-child(5)",
      "#paragraph > p.ad-tips:last-child",
      "#fls",
      "#fi",
      "#lns",
      "#paragraph > div.tougao-user:nth-child(2)",
      "#login-guide-box",
      ".dajia",
      "#paragraph > div.tagging1:last-child",
      "#paragraph > p.ad-tips",
      '[id^="ad-id-"]',
      "div.-hongbao-container.bb:nth-child(6)",
    ];

    selectors.forEach((selector) => {
      document.querySelectorAll(selector).forEach((element) => {
        element.style.display = "none";
      });
    });
  }

  // 函数:图片处理 - 圆角、边框
  function processImage(image) {
    // 这部分匹配到的图片不处理
    if (image.closest("#post_comm")) return;
    if (image.classList.contains("titleLogo")) return;
    if (image.classList.contains("lazy") && image.classList.contains("emoji"))
      return;
    if (
      image.classList.contains("ruanmei-emoji") &&
      image.classList.contains("emoji")
    )
      return;
    if (image.id === "image-viewer" || image.classList.contains("zoomed"))
      return;
    if (image.classList.contains("comment-image")) return;

    // 首页图片
    if (image.closest("a.img")) {
      const anchor = image.closest("a.img");
      if (!anchor.classList.contains("processed")) {
        anchor.style.border = "3px solid #CCC";
        anchor.style.borderRadius = "12px";
        anchor.style.display = "inline-block";
        anchor.style.overflow = "hidden";
        anchor.classList.add("processed");
      }
      // 视频预览图
    } else if (image.closest(".ithome_super_player")) {
      const videoPlayer = image.closest(".ithome_super_player");
      if (!videoPlayer.parentNode.classList.contains("processed")) {
        const wrapper = document.createElement("div");
        wrapper.style.border = "3px solid #CCC";
        wrapper.style.borderRadius = "12px";
        wrapper.style.overflow = "hidden";
        wrapper.style.maxWidth = "100%";
        wrapper.style.display = "block";
        wrapper.style.margin = "0 auto";
        wrapper.classList.add("processed");
        videoPlayer.parentNode.insertBefore(wrapper, videoPlayer);
        wrapper.appendChild(videoPlayer);

        // 视频预览图根据父元素高度调整
        const img = videoPlayer.querySelector("img");
        if (img) {
          const imgWidth = img.getAttribute("w");
          const imgHeight = img.getAttribute("h");
          const parentHeight = wrapper.offsetHeight;

          if (imgWidth > wrapper.offsetWidth) {
            const aspectRatio = imgWidth / imgHeight;
            img.style.height = `${parentHeight}px`;
            img.style.width = `${parentHeight * aspectRatio}px`;
            img.style.objectFit = "cover";
          } else {
            img.style.width = `${imgWidth}px`;
            img.style.height = `${imgHeight}px`;
          }
        }
      }
    } else {
      // 超长图片宽度 450px
      if (image.height > 1000) {
        image.style.borderRadius = "12px";
        image.style.border = "3px solid #CCC";
        image.style.width = "400px";
        image.style.maxWidth = "400px";
        image.style.height = "auto";
        image.style.objectFit = "cover";
        image.style.overflow = "hidden";
        // 常规图片宽度 450px
      } else {
        image.style.borderRadius = "12px";
        image.style.border = "3px solid #CCC";
        image.style.maxWidth = "450px";
      }
    }
  }

  // [调用] 图片处理
  function setRoundedImages() {
    document.querySelectorAll("img").forEach((image) => processImage(image));
  }

  // 函数:头像处理
  function styleHeaderImage() {
    const headerImages = document.querySelectorAll(".list .entry .headerimage");

    headerImages.forEach((image) => {
      image.style.borderRadius = "12px";
      image.style.border = "3px solid #CCC";
    });
  }

  // 函数:多图连续排列时插入间隔
  function wrapImagesInP() {
    if (window.location.href.startsWith("https://www.ithome.com/blog/")) return;
    document.querySelectorAll("img").forEach((image) => {
      // 这部分匹配到的图片不处理
      if (image.closest("#post_comm")) return;
      if (image.closest(".ithome_super_player")) return;
      if (
        image.classList.contains("ruanmei-emoji") &&
        image.classList.contains("emoji")
      )
        return;
      if (image.classList.contains("ithome_super_player")) return;
      if (
        image.parentNode.tagName.toLowerCase() === "p" &&
        image.parentNode.children.length === 1
      )
        return;
      if (image.width < 25 || image.height < 20) return;

      const p = document.createElement("p");
      p.style.textAlign = "center";
      p.style.margin = "0";
      p.setAttribute("data-vmark", "f5e8");
      image.parentNode.insertBefore(p, image);
      p.appendChild(image);
    });
  }

  // 函数:视频处理 - 圆角、边框
  function processIframes() {
    const iframes = document.querySelectorAll(
      '.content .post_content iframe.ithome_video, .content .post_content iframe[src*="player.bilibili.com"]',
    );

    iframes.forEach((iframe) => {
      if (!iframe.classList.contains("processed")) {
        iframe.style.borderRadius = "12px";
        iframe.style.border = "3px solid #CCC";
        iframe.style.display = "block";
        iframe.style.margin = "0 auto";
        iframe.style.overflow = "hidden";
        iframe.classList.add("processed");
      }
    });
  }

  // 函数:页面样式圆角
  function setRounded() {
    const roundeds = document.querySelectorAll(
      ".comm_list ul.list li.entry ul.reply, .content .post_content blockquote, " +
        ".add_comm input#btnComment, .card, span.card",
    );
    roundeds.forEach((rounded) => (rounded.style.borderRadius = "12px"));

    document.querySelectorAll(".add_comm").forEach((addCommElement) => {
      addCommElement.style.borderRadius = "0px 0px 12px 12px";
    });

    document.querySelectorAll(".card, span.card").forEach((card) => {
      card.style.borderRadius = "12px";
      card.style.transform = "scale(0.8)";
    });
  }

  // 函数:移除首页信息流广告
  function removeAds() {
    document
      .querySelectorAll("div.bb.clearfix > div.fl > ul.bl > li")
      .forEach((element) => {
        if (element.querySelector("div.c > div.m:empty")) element.remove();
      });
  }

  // 函数:自动点击「加载更多」按钮
  function autoClickLoadMore() {
    const loadMoreButton = document.querySelector("a.more");

    if (loadMoreButton) {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            loadMoreButton.click();
          }
        });
      });

      observer.observe(loadMoreButton);

      // 监听DOM变化
      const mutationObserver = new MutationObserver(() => {
        const newLoadMoreButton = document.querySelector("a.more");
        if (newLoadMoreButton && !observer.observe(newLoadMoreButton)) {
          observer.observe(newLoadMoreButton);
        }
      });

      mutationObserver.observe(document.body, {
        childList: true,
        subtree: true,
      });
    }
  }

  // 函数:评论加载
  function forceLoadComments() {
    const footer = document.querySelector("#post_comm");

    const spacer = document.createElement("div");
    spacer.style.height = "100vh";
    spacer.style.visibility = "hidden";
    document.body.appendChild(spacer);

    window.scrollTo(0, document.body.scrollHeight);

    spacer.remove();
    window.scrollTo(0, 0);
  }

  // 函数:首页卡片样式
  function initializePage() {
    function makeListItemsClickable() {
      const listItems = document.querySelectorAll(".bl > li");

      listItems.forEach((li) => {
        const wrapper = document.createElement("div");
        wrapper.classList.add("hover-wrapper");
        wrapper.style.position = "relative";
        wrapper.style.padding = "12px 16px";
        wrapper.style.borderRadius = "12px";
        wrapper.style.overflow = "hidden";
        wrapper.style.margin = "16px 0";

        li.parentNode.insertBefore(wrapper, li);
        wrapper.appendChild(li);

        const titleLink = li.querySelector("h2 a");

        if (titleLink) {
          const titleText = document.createTextNode(titleLink.textContent);
          titleLink.replaceWith(titleText);

          wrapper.style.cursor = "pointer";
          wrapper.addEventListener("click", () => {
            window.open(titleLink.href, titleLink.target || "_self");
          });

          wrapper.addEventListener("mouseover", () => {
            wrapper.style.boxShadow = "0px 6px 15px rgba(0, 0, 0, 0.2)";
            wrapper.style.backgroundColor = getBackgroundColor();
          });

          wrapper.addEventListener("mouseout", () => {
            wrapper.style.boxShadow = "none";
            wrapper.style.backgroundColor = "transparent";
          });
        }
      });
    }

    function setHome() {
      const divs = document.querySelectorAll("div.fl");
      divs.forEach((div) => {
        div.style.width = "870px";
      });
    }

    function removeMarginTop() {
      const hoverWrappers = document.querySelectorAll(".hover-wrapper");
      hoverWrappers.forEach((hoverWrapper) => {
        const listItems = hoverWrapper.querySelectorAll("li");
        listItems.forEach((item) => {
          item.style.marginTop = "0";
        });
      });
    }

    function setDivWidthTo590() {
      const divs = document.querySelectorAll("div.c");
      divs.forEach((div) => {
        div.style.width = "640px";
      });
    }

    function getBackgroundColor() {
      if (
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches
      ) {
        return "#333333";
      } else {
        return "#f9f9f9";
      }
    }

    makeListItemsClickable();
    setHome();
    removeMarginTop();
    setDivWidthTo590();
  }

  // 函数:评论区图片放大
  function replaceImageWrapper() {
    const imageWrappers = document.querySelectorAll(
      ".post-img-list a.img-wrapper",
    );

    imageWrappers.forEach((wrapper) => {
      const img = wrapper.querySelector("img");
      if (img) {
        const parent = wrapper.parentElement;

        wrapper.classList.remove("img-wrapper");
        wrapper.classList.add("img-click");

        wrapper.removeAttribute("href");

        img.style.width = "30%";
        img.style.height = "auto";
        img.style.borderRadius = "12px";
        img.style.border = "3px solid #CCC";

        let isZoomed = false;

        img.addEventListener("click", () => {
          if (isZoomed) {
            img.style.width = "30%";
          } else {
            img.style.width = "100%";
          }
          img.style.height = "auto";
          isZoomed = !isZoomed;
        });
      }
    });
  }

  // 函数:观察DOM变化,处理新刷出的内容
  function observeDOM() {
    const observer = new MutationObserver((mutationsList) => {
      for (const mutation of mutationsList) {
        if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
          wrapImagesInP();
          setRounded();
          removeAds();
          hideElements();
          setRoundedImages();
          styleHeaderImage();
          initializePage();
          replaceImageWrapper();
        }
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  }

  // 监听事件
  window.addEventListener("scroll", autoClickLoadMore);
  window.addEventListener("load", function () {
    hideElements();
    forceLoadComments();
    removeAds();
    wrapImagesInP();
    setRounded();
    processIframes();
    setRoundedImages();
    styleHeaderImage();
    initializePage();
    replaceImageWrapper();
    observeDOM();
    document.body.style.opacity = "1";

    // 处理图片懒加载
    document.querySelectorAll("img").forEach((img) => {
      if (img.hasAttribute("loading")) {
        img.removeAttribute("loading");
      }
      if (img.classList.contains("lazy")) {
        img.classList.remove("lazy");
      }
      if (img.dataset.src) {
        img.src = img.dataset.src;
      }
      if (img.dataset.original) {
        img.src = img.dataset.original;
      }
      img.loading = "eager";
    });
  });
})();