简书优化

支持手机端和PC端,屏蔽广告,优化浏览体验,自动跳转拦截的URL

目前为 2024-04-06 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 简书优化
  3. // @icon https://www.jianshu.com/favicon.ico
  4. // @namespace https://gf.qytechs.cn/zh-CN/scripts/485483
  5. // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
  6. // @version 2024.4.6
  7. // @license MIT
  8. // @description 支持手机端和PC端,屏蔽广告,优化浏览体验,自动跳转拦截的URL
  9. // @author WhiteSevs
  10. // @match *://*.jianshu.com/*
  11. // @match *://*.jianshu.io/*
  12. // @grant GM_registerMenuCommand
  13. // @grant GM_unregisterMenuCommand
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @grant GM_deleteValue
  17. // @grant GM_info
  18. // @grant unsafeWindow
  19. // @run-at document-start
  20. // @require https://update.gf.qytechs.cn/scripts/456485/1352602/pops.js
  21. // @require https://update.gf.qytechs.cn/scripts/455186/1355010/WhiteSevsUtils.js
  22. // @require https://update.gf.qytechs.cn/scripts/465772/1344519/DOMUtils.js
  23. // ==/UserScript==
  24.  
  25. (function () {
  26. if (typeof unsafeWindow === "undefined") {
  27. unsafeWindow = globalThis;
  28. }
  29. /**
  30. * @type {import("../库/pops")}
  31. */
  32. const pops = window.pops;
  33. /**
  34. * @type {import("../库/Utils")}
  35. */
  36. const utils = window.Utils.noConflict();
  37. /**
  38. * @type {import("../库/DOMUtils")}
  39. */
  40. const DOMUtils = window.DOMUtils.noConflict();
  41. const log = new utils.Log(GM_info, unsafeWindow.console || console);
  42. log.config({
  43. autoClearConsole: false,
  44. });
  45. const GM_addStyle = utils.addStyle;
  46. /**
  47. * 菜单
  48. */
  49. const GM_Menu = new utils.GM_Menu({
  50. GM_getValue,
  51. GM_setValue,
  52. GM_registerMenuCommand,
  53. GM_unregisterMenuCommand,
  54. });
  55. /**
  56. * 移除元素(未出现也可以等待出现)
  57. * @param {string} selectorText 元素选择器
  58. */
  59. const waitForElementToRemove = function (selectorText = "") {
  60. utils.waitNodeList(selectorText).then((nodeList) => {
  61. nodeList.forEach((item) => {
  62. item.remove();
  63. });
  64. });
  65. };
  66.  
  67. const Optimization = {
  68. jianshu: {
  69. /**
  70. * 函数入口
  71. */
  72. init() {
  73. if (utils.isPhone()) {
  74. log.success("简书-移动端");
  75. this.Mobile.run();
  76. } else {
  77. log.success("简书-桌面端");
  78. this.PC.run();
  79. }
  80. },
  81. PC: {
  82. /**
  83. * 添加屏蔽CSS
  84. */
  85. addCSS() {
  86. GM_addStyle(`
  87. .download-app-guidance,
  88. .call-app-btn,
  89. .collapse-tips,
  90. .note-graceful-button,
  91. .app-open,
  92. .header-wrap,
  93. .recommend-wrap.recommend-ad,
  94. .call-app-Ad-bottom,
  95. #recommended-notes p.top-title span.more,
  96. #homepage .modal,
  97. button.index_call-app-btn,
  98. span.note__flow__download,
  99. .download-guide,
  100. #footer,
  101. .comment-open-app-btn-wrap,
  102. .nav.navbar-nav + div,
  103. .self-flow-ad,
  104. #free-reward-panel,
  105. div[id*='AdFive'],
  106. #index-aside-download-qrbox,
  107. .baidu-app-download-2eIkf_1,
  108. /* 底部的"小礼物走一走,来简书关注我"、赞赏支持和更多精彩内容,就在简书APP */
  109. div[role="main"] > div > section:first-child > div:nth-last-child(2),
  110. /* 它的内部是script标签,可能影响部分评论之间的高度问题 */
  111. div.adad_container{
  112. display:none !important;
  113. }
  114. body.reader-day-mode.normal-size {
  115. overflow: auto !important;
  116. }
  117. .collapse-free-content{
  118. height:auto !important;
  119. }
  120. .copyright{
  121. color:#000 !important;
  122. }
  123. #note-show .content .show-content-free .collapse-free-content:after{
  124. background-image:none !important;
  125. }
  126. footer > div > div{
  127. justify-content: center;
  128. }
  129. /* 修复底部最后编辑于:。。。在某些套壳浏览器上的错位问题 */
  130. #note-show .content .show-content-free .note-meta-time{
  131. margin-top: 0px !important;
  132. }
  133. `);
  134. },
  135. /**
  136. * 全文居中
  137. */
  138. articleCenter() {
  139. log.success("全文居中");
  140. GM_addStyle(`
  141. div[role=main] aside,
  142. div._3Pnjry{
  143. display: none !important;
  144. }
  145. div._gp-ck{
  146. width: 100% !important;
  147. }`);
  148. waitForElementToRemove("div[role=main] aside");
  149. waitForElementToRemove("div._3Pnjry");
  150. utils.waitNodeList("div._gp-ck").then((nodeList) => {
  151. nodeList.forEach((item) => {
  152. item.style["width"] = "100%";
  153. });
  154. });
  155. },
  156. /**
  157. * 去除剪贴板劫持
  158. */
  159. removeClipboardHijacking() {
  160. log.success("去除剪贴板劫持");
  161. const stopNativePropagation = (event) => {
  162. event.stopPropagation();
  163. };
  164. window.addEventListener("copy", stopNativePropagation, true);
  165. document.addEventListener("copy", stopNativePropagation, true);
  166. },
  167. /**
  168. * 自动展开全文
  169. */
  170. autoExpandFullText() {
  171. utils
  172. .waitNode(`div#homepage div[class*="dialog-"]`)
  173. .then((element) => {
  174. element.style["visibility"] = "hidden";
  175. utils.mutationObserver(element, {
  176. callback: (mutations) => {
  177. if (mutations.length == 0) {
  178. return;
  179. }
  180. if (mutations.target.style["display"] != "none") {
  181. log.success("自动展开全文");
  182. document
  183. .querySelector(
  184. 'div#homepage div[class*="dialog-"] .cancel'
  185. )
  186. ?.click();
  187. }
  188. },
  189. config: {
  190. /* 子节点的变动(新增、删除或者更改) */
  191. childList: false,
  192. /* 属性的变动 */
  193. attributes: true,
  194. /* 节点内容或节点文本的变动 */
  195. characterData: true,
  196. /* 是否将观察器应用于该节点的所有后代节点 */
  197. subtree: true,
  198. },
  199. });
  200. });
  201. },
  202. /**
  203. * 去除简书拦截其它网址的url并自动跳转
  204. */
  205. jumpRedirect() {
  206. if (window.location.pathname === "/go-wild") {
  207. /* 禁止简书拦截跳转 */
  208. window.stop();
  209. let search = window.location.href.replace(
  210. window.location.origin + "/",
  211. ""
  212. );
  213. search = decodeURIComponent(search);
  214. let newURL = search
  215. .replace(/^go-wild\?ac=2&url=/gi, "")
  216. .replace(/^https:\/\/link.zhihu.com\/\?target\=/gi, "");
  217. window.location.href = newURL;
  218. }
  219. },
  220. /**
  221. * 屏蔽相关文章
  222. */
  223. shieldRelatedArticles() {
  224. log.success("屏蔽相关文章");
  225. GM_addStyle(`
  226. div[role="main"] > div > section:nth-child(2){
  227. display: none !important;
  228. }
  229. `);
  230. },
  231. /**
  232. * 【屏蔽】客户端弹窗
  233. */
  234. shieldClientDialog() {
  235. GM_addStyle(`
  236. div:has(>div[class*="-mask"]:not([class*="-mask-hidden"]) + div[tabindex="-1"][role="dialog"]){
  237. display: none !important;
  238. }
  239. `);
  240. utils
  241. .waitNode(
  242. `div[class*="-mask"]:not([class*="-mask-hidden"]) + div[tabindex="-1"][role="dialog"]`
  243. )
  244. .then((element) => {
  245. log.info("弹窗出现");
  246. utils
  247. .waitPropertyByInterval(
  248. element,
  249. () => {
  250. let react = utils.getReactObj(element);
  251. return react?.reactInternalInstance?.return?.return
  252. ?.memoizedProps?.onClose;
  253. },
  254. 250,
  255. 10000
  256. )
  257. .then(() => {
  258. let react = utils.getReactObj(element);
  259. react.reactInternalInstance.return.return.memoizedProps.onClose(
  260. new Event("click")
  261. );
  262. log.success("调用函数关闭弹窗");
  263. });
  264. });
  265. },
  266. /**
  267. * 屏蔽评论区
  268. */
  269. shieldUserComments() {
  270. log.success("屏蔽评论区");
  271. GM_addStyle(`
  272. div#note-page-comment{
  273. display: none !important;
  274. }
  275. `);
  276. },
  277. /**
  278. * 屏蔽底部推荐阅读
  279. */
  280. shieldRecommendedReading() {
  281. log.success("屏蔽底部推荐阅读");
  282. GM_addStyle(`
  283. div[role="main"] > div > section:last-child{
  284. display: none !important;
  285. }
  286. `);
  287. },
  288. run() {
  289. this.addCSS();
  290. if (PopsPanel.getValue("JianShuAutoJumpRedirect_PC")) {
  291. Optimization.jianshu.PC.jumpRedirect();
  292. }
  293. if (PopsPanel.getValue("JianShuRemoveClipboardHijacking")) {
  294. this.removeClipboardHijacking();
  295. }
  296. if (PopsPanel.getValue("JianShuAutoExpandFullText")) {
  297. this.autoExpandFullText();
  298. }
  299. if (PopsPanel.getValue("JianShuArticleCenter")) {
  300. this.articleCenter();
  301. }
  302. if (PopsPanel.getValue("JianShuShieldRelatedArticles")) {
  303. this.shieldRelatedArticles();
  304. }
  305. if (PopsPanel.getValue("jianshu-shieldClientDialog")) {
  306. this.shieldClientDialog();
  307. }
  308. if (PopsPanel.getValue("JianShuShieldUserComments")) {
  309. this.shieldUserComments();
  310. }
  311. if (PopsPanel.getValue("JianShuShieldRecommendedReading")) {
  312. this.shieldRecommendedReading();
  313. }
  314. },
  315. },
  316. Mobile: {
  317. addCSS() {
  318. Optimization.jianshu.PC.addCSS();
  319. },
  320. /**
  321. * 手机-屏蔽底部推荐阅读
  322. */
  323. removeFooterRecommendRead() {
  324. log.success("屏蔽底部推荐阅读");
  325. GM_addStyle(`
  326. #recommended-notes{
  327. display: none !important;
  328. }`);
  329. },
  330. /**
  331. * 处理原型
  332. */
  333. handlePrototype() {
  334. log.success("处理原型添加script标签");
  335. let originalAppendChild = Node.prototype.appendChild;
  336. Node.prototype.appendChild = function (element) {
  337. /* 允许添加的元素localName */
  338. let allowElementLocalNameList = ["img"];
  339. /* 不允许script标签加载包括jianshu.io的js资源,会让简书跳到广告页面 */
  340. if (
  341. element.src &&
  342. !element.src.includes("jianshu.io") &&
  343. !allowElementLocalNameList.includes(element.localName)
  344. ) {
  345. log.success(["禁止添加的元素", element]);
  346. return null;
  347. } else {
  348. return originalAppendChild.call(this, element);
  349. }
  350. };
  351. },
  352. /**
  353. * 屏蔽评论区
  354. */
  355. shieldUserComments() {
  356. log.success("屏蔽评论区");
  357. GM_addStyle(`
  358. #comment-main{
  359. display: none !important;
  360. }
  361. `);
  362. },
  363. run() {
  364. this.addCSS();
  365. if (PopsPanel.getValue("JianShuAutoJumpRedirect_Mobile")) {
  366. Optimization.jianshu.PC.jumpRedirect();
  367. }
  368. if (PopsPanel.getValue("JianShuHijackSchemeScriptLabel_Mobile")) {
  369. this.handlePrototype();
  370. }
  371. if (PopsPanel.getValue("JianShuRemoveClipboardHijacking_Mobile")) {
  372. Optimization.jianshu.PC.removeClipboardHijacking();
  373. }
  374.  
  375. if (PopsPanel.getValue("JianShuAutoExpandFullText_Mobile")) {
  376. Optimization.jianshu.PC.autoExpandFullText();
  377. }
  378. if (PopsPanel.getValue("JianShuremoveFooterRecommendRead")) {
  379. this.removeFooterRecommendRead();
  380. }
  381. if (PopsPanel.getValue("JianShuShieldUserCommentsMobile")) {
  382. this.shieldUserComments();
  383. }
  384. },
  385. },
  386. },
  387. };
  388.  
  389. /**
  390. * 配置面板
  391. */
  392. const PopsPanel = {
  393. /**
  394. * 本地存储的总键名
  395. */
  396. key: "GM_Panel",
  397. /**
  398. * 属性attributes的data-key
  399. */
  400. attributeDataKey_Name: "data-key",
  401. /**
  402. * 属性attributes的data-default-value
  403. */
  404. attributeDataDefaultValue_Name: "data-default-value",
  405. /**
  406. * 初始化菜单
  407. */
  408. initMenu() {
  409. this.initLocalDefaultValue();
  410. if (unsafeWindow.top !== unsafeWindow.self) {
  411. return;
  412. }
  413. GM_Menu.add([
  414. {
  415. key: "show_pops_panel_setting",
  416. text: "⚙ 设置",
  417. autoReload: false,
  418. isStoreValue: false,
  419. showText(text) {
  420. return text;
  421. },
  422. callback: () => {
  423. this.showPanel();
  424. },
  425. },
  426. ]);
  427. },
  428. /**
  429. * 初始化本地设置默认的值
  430. */
  431. initLocalDefaultValue() {
  432. let content = this.getContent();
  433. content.forEach((item) => {
  434. if (!item["forms"]) {
  435. return;
  436. }
  437. item.forms.forEach((__item__) => {
  438. if (__item__.forms) {
  439. __item__.forms.forEach((containerItem) => {
  440. if (!containerItem.attributes) {
  441. return;
  442. }
  443. let key = containerItem.attributes[this.attributeDataKey_Name];
  444. let defaultValue =
  445. containerItem.attributes[this.attributeDataDefaultValue_Name];
  446. if (this.getValue(key) == null) {
  447. this.setValue(key, defaultValue);
  448. }
  449. });
  450. } else {
  451. }
  452. });
  453. });
  454. },
  455. /**
  456. * 设置值
  457. * @param {string} key 键
  458. * @param {any} value 值
  459. */
  460. setValue(key, value) {
  461. let localValue = GM_getValue(this.key, {});
  462. localValue[key] = value;
  463. GM_setValue(this.key, localValue);
  464. },
  465. /**
  466. * 获取值
  467. * @param {string} key 键
  468. * @param {any} defaultValue 默认值
  469. * @returns {any}
  470. */
  471. getValue(key, defaultValue) {
  472. let localValue = GM_getValue(this.key, {});
  473. return localValue[key] ?? defaultValue;
  474. },
  475. /**
  476. * 删除值
  477. * @param {string} key 键
  478. */
  479. deleteValue(key) {
  480. let localValue = GM_getValue(this.key, {});
  481. delete localValue[key];
  482. GM_setValue(this.key, localValue);
  483. },
  484. /**
  485. * 显示设置面板
  486. */
  487. showPanel() {
  488. pops.panel({
  489. title: {
  490. text: `${GM_info?.script?.name || "简书优化"}-设置`,
  491. position: "center",
  492. },
  493. content: this.getContent(),
  494. mask: {
  495. enable: true,
  496. clickEvent: {
  497. toClose: true,
  498. },
  499. },
  500. width: pops.isPhone() ? "92vw" : "800px",
  501. height: pops.isPhone() ? "80vh" : "600px",
  502. only: true,
  503. drag: true,
  504. });
  505. },
  506. /**
  507. * 获取按钮配置
  508. * @param {string} text 文字
  509. * @param {string} key 键
  510. * @param {boolean} defaultValue 默认值
  511. * @param {?(event:Event,value: boolean)=>boolean} _callback_ 点击回调
  512. * @param {string|undefined} description 描述
  513. */
  514. getSwtichDetail(text, key, defaultValue, _callback_, description) {
  515. /**
  516. * @type {PopsPanelSwitchDetails}
  517. */
  518. let result = {
  519. text: text,
  520. type: "switch",
  521. description: description,
  522. attributes: {},
  523. getValue() {
  524. return Boolean(PopsPanel.getValue(key, defaultValue));
  525. },
  526. callback(event, value) {
  527. log.success(`${value ? "开启" : "关闭"} ${text}`);
  528. if (typeof _callback_ === "function") {
  529. if (_callback_(event, value)) {
  530. return;
  531. }
  532. }
  533. PopsPanel.setValue(key, value);
  534. },
  535. };
  536. result.attributes[this.attributeDataKey_Name] = key;
  537. result.attributes[this.attributeDataDefaultValue_Name] =
  538. Boolean(defaultValue);
  539. return result;
  540. },
  541. /**
  542. * 获取配置内容
  543. */
  544. getContent() {
  545. return [
  546. {
  547. id: "jianshu-panel-config-pc",
  548. title: "桌面端",
  549. forms: [
  550. {
  551. text: "屏蔽",
  552. type: "forms",
  553. forms: [
  554. PopsPanel.getSwtichDetail(
  555. "【屏蔽】底部推荐阅读",
  556. "JianShuShieldRecommendedReading",
  557. false
  558. ),
  559. PopsPanel.getSwtichDetail(
  560. "【屏蔽】评论区",
  561. "JianShuShieldUserComments",
  562. false
  563. ),
  564. PopsPanel.getSwtichDetail(
  565. "【屏蔽】相关文章",
  566. "JianShuShieldRelatedArticles",
  567. false
  568. ),
  569. PopsPanel.getSwtichDetail(
  570. "【屏蔽】客户端弹窗",
  571. "jianshu-shieldClientDialog",
  572. false,
  573. void 0,
  574. "弹出的【扫码安装简书客户端 畅享全文阅读体验】"
  575. ),
  576. ],
  577. },
  578. {
  579. text: "功能",
  580. type: "forms",
  581. forms: [
  582. PopsPanel.getSwtichDetail(
  583. "全文居中",
  584. "JianShuArticleCenter",
  585. true
  586. ),
  587. PopsPanel.getSwtichDetail(
  588. "自动展开全文",
  589. "JianShuAutoExpandFullText",
  590. true
  591. ),
  592. PopsPanel.getSwtichDetail(
  593. "重定向链接",
  594. "JianShuAutoJumpRedirect_PC",
  595. true,
  596. undefined,
  597. "自动跳转简书拦截的Url链接"
  598. ),
  599. ],
  600. },
  601. {
  602. text: "劫持/拦截",
  603. type: "forms",
  604. forms: [
  605. PopsPanel.getSwtichDetail(
  606. "拦截-剪贴板",
  607. "JianShuRemoveClipboardHijacking",
  608. true,
  609. undefined,
  610. "去除禁止复制"
  611. ),
  612. ],
  613. },
  614. ],
  615. },
  616. {
  617. id: "jianshu-panel-config-mobile",
  618. title: "移动端",
  619. forms: [
  620. {
  621. text: "屏蔽",
  622. type: "forms",
  623. forms: [
  624. PopsPanel.getSwtichDetail(
  625. "【屏蔽】底部推荐阅读",
  626. "JianShuremoveFooterRecommendRead",
  627. false
  628. ),
  629. PopsPanel.getSwtichDetail(
  630. "【屏蔽】评论区",
  631. "JianShuShieldUserCommentsMobile",
  632. false
  633. ),
  634. ],
  635. },
  636. {
  637. text: "功能",
  638. type: "forms",
  639. forms: [
  640. PopsPanel.getSwtichDetail(
  641. "自动展开全文",
  642. "JianShuAutoExpandFullText_Mobile",
  643. true
  644. ),
  645. PopsPanel.getSwtichDetail(
  646. "重定向链接",
  647. "JianShuAutoJumpRedirect_Mobile",
  648. true,
  649. undefined,
  650. "自动跳转简书拦截的Url链接"
  651. ),
  652. ],
  653. },
  654. {
  655. text: "劫持/拦截",
  656. type: "forms",
  657. forms: [
  658. PopsPanel.getSwtichDetail(
  659. "拦截-剪贴板",
  660. "JianShuRemoveClipboardHijacking_Mobile",
  661. true,
  662. undefined,
  663. "去除禁止复制"
  664. ),
  665. PopsPanel.getSwtichDetail(
  666. "劫持-唤醒/跳转App",
  667. "JianShuHijackSchemeScriptLabel_Mobile",
  668. true,
  669. undefined,
  670. "去除简书唤醒调用App"
  671. ),
  672. ],
  673. },
  674. ],
  675. },
  676. ];
  677. },
  678. };
  679.  
  680. PopsPanel.initMenu();
  681.  
  682. Optimization.jianshu.init();
  683. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址