知乎增强

移除登录弹窗、屏蔽首页视频、默认收起回答、快捷收起当前回答/评论(左键两侧空白处)、快捷回到顶部(右键两侧空白处)、屏蔽用户 (发布的内容)、屏蔽关键词(标题/评论)、移除高亮链接、屏蔽盐选内容、净化标题消息、展开问题描述、置顶显示时间、完整问题时间、区分问题文章、直达问题按钮、默认高清原图、默认站外直链

当前为 2022-01-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 知乎增强
  3. // @version 1.8.9
  4. // @author X.I.U
  5. // @description 移除登录弹窗、屏蔽首页视频、默认收起回答、快捷收起当前回答/评论(左键两侧空白处)、快捷回到顶部(右键两侧空白处)、屏蔽用户 (发布的内容)、屏蔽关键词(标题/评论)、移除高亮链接、屏蔽盐选内容、净化标题消息、展开问题描述、置顶显示时间、完整问题时间、区分问题文章、直达问题按钮、默认高清原图、默认站外直链
  6. // @match *://www.zhihu.com/*
  7. // @match *://zhuanlan.zhihu.com/*
  8. // @icon https://static.zhihu.com/heifetz/favicon.ico
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_unregisterMenuCommand
  12. // @grant GM_openInTab
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_notification
  16. // @grant GM_info
  17. // @license GPL-3.0 License
  18. // @run-at document-end
  19. // @incompatible safari
  20. // @namespace https://greasyfork.org/scripts/4122051
  21. // @supportURL https://github.com/XIU2/UserScript
  22. // @homepageURL https://github.com/XIU2/UserScript
  23. // ==/UserScript==
  24.  
  25. 'use strict';
  26. var menu_ALL = [
  27. ['menu_defaultCollapsedAnswer', '默认收起回答', '默认收起回答', true],
  28. ['menu_collapsedAnswer', '一键收起回答', '一键收起回答', true],
  29. ['menu_collapsedNowAnswer', '收起当前回答/评论 (点击两侧空白处)', '收起当前回答/评论', true],
  30. ['menu_backToTop', '快捷回到顶部 (右键两侧空白处)', '快捷回到顶部', true],
  31. ['menu_blockUsers', '屏蔽指定用户', '屏蔽指定用户', true],
  32. ['menu_customBlockUsers', '自定义屏蔽用户', '自定义屏蔽用户', ['故事档案局', '盐选推荐', '盐选科普', '盐选成长计划', '知乎盐选会员', '知乎盐选创作者', '盐选心理', '盐选健康必修课', '盐选奇妙物语', '盐选生活馆', '盐选职场', '盐选文学甄选', '盐选作者小管家', '盐选博物馆', '盐选点金', '盐选测评室', '盐选科技前沿', '盐选会员精品']],
  33. ['menu_blockKeywords', '屏蔽指定关键词', '屏蔽指定关键词', true],
  34. ['menu_customBlockKeywords', '自定义屏蔽关键词', '自定义屏蔽关键词', []],
  35. ['menu_blockType', '屏蔽指定类别 (视频/文章等)', '勾选 = 屏蔽该类别的信息流', ''],
  36. ['menu_blockTypeVideo', '视频 [首页、搜索页、问题页]', '视频(首页、搜索页、问题页)', true],
  37. ['menu_blockTypeArticle', '文章 [首页、搜索页]', '文章(首页、搜索页)', false],
  38. ['menu_blockTypeTopic', '话题 [搜索页]', '话题(搜索页)', false],
  39. ['menu_blockTypeSearch', '杂志文章、相关搜索等 [搜索页]', '相关搜索、杂志等(搜索页)', false],
  40. ['menu_blockYanXuan', '屏蔽盐选内容', '屏蔽盐选内容', false],
  41. ['menu_cleanTitles', '净化标题消息 (标题中的私信/消息)', '净化标题提醒', false],
  42. ['menu_questionRichTextMore', '展开问题描述', '展开问题描述', false],
  43. ['menu_publishTop', '置顶显示时间', '置顶显示时间', true],
  44. ['menu_typeTips', '区分问题文章', '区分问题文章', true],
  45. ['menu_toQuestion', '直达问题按钮', '直达问题按钮', true]
  46. ], menu_ID = [];
  47. for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
  48. if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
  49. }
  50. registerMenuCommand();
  51.  
  52. // 注册脚本菜单
  53. function registerMenuCommand() {
  54. if (menu_ID.length > menu_ALL.length){ // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
  55. for (let i=0;i<menu_ID.length;i++){
  56. GM_unregisterMenuCommand(menu_ID[i]);
  57. }
  58. }
  59. for (let i=0;i<menu_ALL.length;i++){ // 循环注册脚本菜单
  60. menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
  61. if (menu_ALL[i][0] === 'menu_customBlockUsers') {
  62. if (menu_value('menu_blockUsers')) menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function(){customBlockUsers()});
  63. } else if (menu_ALL[i][0] === 'menu_customBlockKeywords') {
  64. if (menu_value('menu_blockKeywords')) menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function(){customBlockKeywords()});
  65. } else if (menu_ALL[i][0] === 'menu_blockType') {
  66. menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function(){menu_setting('checkbox', menu_ALL[i][1], menu_ALL[i][2], true, [menu_ALL[i+1], menu_ALL[i+2], menu_ALL[i+3], menu_ALL[i+4]])});
  67. } else if (menu_ALL[i][0] != 'menu_blockTypeVideo' && menu_ALL[i][0] != 'menu_blockTypeArticle' && menu_ALL[i][0] != 'menu_blockTypeTopic' && menu_ALL[i][0] != 'menu_blockTypeSearch') {
  68. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3]?'✅':'❌'} ${menu_ALL[i][1]}`, function(){menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`)});
  69. }
  70. }
  71. menu_ID[menu_ID.length] = GM_registerMenuCommand('💬 反馈 & 建议', function () {window.GM_openInTab('https://github.com/XIU2/UserScript#xiu2userscript', {active: true,insert: true,setParent: true});window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/419081/feedback', {active: true,insert: true,setParent: true});});
  72. }
  73.  
  74.  
  75. // 菜单开关
  76. function menu_switch(menu_status, Name, Tips) {
  77. if (menu_status == 'true'){
  78. GM_setValue(`${Name}`, false);
  79. GM_notification({text: `已关闭 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
  80. }else{
  81. GM_setValue(`${Name}`, true);
  82. GM_notification({text: `已开启 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
  83. }
  84. registerMenuCommand(); // 重新注册脚本菜单
  85. };
  86.  
  87.  
  88. // 返回菜单值
  89. function menu_value(menuName) {
  90. for (let menu of menu_ALL) {
  91. if (menu[0] == menuName) {
  92. return menu[3]
  93. }
  94. }
  95. }
  96.  
  97.  
  98. // 脚本设置
  99. function menu_setting(type, title, tips, line, menu) {
  100. let _br = '', _html = `<style class="zhihuE_SettingStyle">.zhihuE_SettingRoot {position: absolute;top: 50%;left: 50%;-webkit-transform: translate(-50%, -50%);-moz-transform: translate(-50%, -50%);-ms-transform: translate(-50%, -50%);-o-transform: translate(-50%, -50%);transform: translate(-50%, -50%);width: auto;min-width: 400px;max-width: 600px;height: auto;min-height: 150px;max-height: 400px;color: #535353;background-color: #fff;border-radius: 3px;}
  101. .zhihuE_SettingBackdrop_1 {position: fixed;top: 0;right: 0;bottom: 0;left: 0;z-index: 203;display: -webkit-box;display: -ms-flexbox;display: flex;-webkit-box-orient: vertical;-webkit-box-direction: normal;-ms-flex-direction: column;flex-direction: column;-webkit-box-pack: center;-ms-flex-pack: center;justify-content: center;overflow-x: hidden;overflow-y: auto;-webkit-transition: opacity .3s ease-out;transition: opacity .3s ease-out;}
  102. .zhihuE_SettingBackdrop_2 {position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 0;background-color: rgba(18,18,18,.65);-webkit-transition: background-color .3s ease-out;transition: background-color .3s ease-out;}
  103. .zhihuE_SettingRoot .zhihuE_SettingHeader {padding: 10px 20px;color: #fff;font-weight: bold;background-color: #3994ff;border-radius: 3px 3px 0 0;}
  104. .zhihuE_SettingRoot .zhihuE_SettingMain {padding: 10px 20px;border-radius: 0 0 3px 3px;}
  105. .zhihuE_SettingHeader span {float: right;cursor: pointer;}
  106. .zhihuE_SettingMain input {margin: 10px 6px 10px 0;cursor: pointer;vertical-align:middle}
  107. .zhihuE_SettingMain label {margin-right: 20px;user-select: none;cursor: pointer;vertical-align:middle}
  108. .zhihuE_SettingMain hr {border: 0.5px solid #f4f4f4;}
  109. [data-theme="dark"] .zhihuE_SettingRoot {color: #adbac7;background-color: #343A44;}
  110. [data-theme="dark"] .zhihuE_SettingHeader {color: #d0d0d0;background-color: #2D333B;}
  111. [data-theme="dark"] .zhihuE_SettingMain hr {border: 0.5px solid #2d333b;}</style>
  112. <div class="zhihuE_SettingBackdrop_1"><div class="zhihuE_SettingBackdrop_2"></div><div class="zhihuE_SettingRoot">
  113. <div class="zhihuE_SettingHeader">${title}<span class="zhihuE_SettingClose" title="点击关闭"><svg class="Zi Zi--Close Modal-closeIcon" fill="currentColor" viewBox="0 0 24 24" width="24" height="24"><path d="M13.486 12l5.208-5.207a1.048 1.048 0 0 0-.006-1.483 1.046 1.046 0 0 0-1.482-.005L12 10.514 6.793 5.305a1.048 1.048 0 0 0-1.483.005 1.046 1.046 0 0 0-.005 1.483L10.514 12l-5.208 5.207a1.048 1.048 0 0 0 .006 1.483 1.046 1.046 0 0 0 1.482.005L12 13.486l5.207 5.208a1.048 1.048 0 0 0 1.483-.006 1.046 1.046 0 0 0 .005-1.482L13.486 12z" fill-rule="evenodd"></path></svg></span></div>
  114. <div class="zhihuE_SettingMain"><p>${tips}</p><hr>`
  115. if (line) _br = '<br>'
  116. for (let i=0; i<menu.length; i++) {
  117. if (GM_getValue(menu[i][0])) {
  118. _html += `<label><input name="zhihuE_Setting" type="checkbox" value="${menu[i][0]}" checked="checked">${menu[i][1]}</label>${_br}`
  119. } else {
  120. _html += `<label><input name="zhihuE_Setting" type="checkbox" value="${menu[i][0]}">${menu[i][1]}</label>${_br}`
  121. }
  122. }
  123. _html += `</div></div></div>`
  124. document.body.insertAdjacentHTML('beforeend', _html); // 插入网页末尾
  125. setTimeout(function() { // 延迟 100 毫秒,避免太快
  126. // 关闭按钮 点击事件
  127. document.querySelector('.zhihuE_SettingClose').onclick = function(){this.parentElement.parentElement.parentElement.remove();document.querySelector('.zhihuE_SettingStyle').remove();}
  128. // 点击周围空白处 = 点击关闭按钮
  129. document.querySelector('.zhihuE_SettingBackdrop_2').onclick = function(event){if (event.target == this) {document.querySelector('.zhihuE_SettingClose').click();};}
  130. // 复选框 点击事件
  131. document.getElementsByName('zhihuE_Setting').forEach(function (checkBox) {
  132. checkBox.addEventListener('click', function(){if (this.checked) {GM_setValue(this.value, true);} else {GM_setValue(this.value, false);}});
  133. })
  134. }, 100)
  135. }
  136.  
  137.  
  138. // 添加收起回答观察器
  139. function getCollapsedAnswerObserver() {
  140. if (!window._collapsedAnswerObserver) {
  141. const observer = new MutationObserver(mutations => {
  142. for (const mutation of mutations) {
  143. if (mutation.target.hasAttribute('script-collapsed')) return
  144. if (!mutation.target.classList.contains('RichContent')) continue
  145. for (const addedNode of mutation.addedNodes) {
  146. if (addedNode.nodeType != Node.ELEMENT_NODE) continue
  147. //console.log(addedNode, addedNode.offsetHeight)
  148. if (addedNode.className == 'RichContent-inner' && addedNode.offsetHeight < 400) return
  149. //console.log(addedNode.offsetHeight)
  150. const button = addedNode.querySelector('.ContentItem-actions.Sticky [data-zop-retract-question]');
  151. if (button) {
  152. mutation.target.setAttribute('script-collapsed', '');
  153. button.click();
  154. return
  155. }
  156. }
  157. }
  158. })
  159.  
  160. observer.start = function() {
  161. if (!this._active) {
  162. this.observe(document, { childList: true, subtree: true });
  163. this._active = true;
  164. }
  165. }
  166. observer.end = function() {
  167. if (this._active) {
  168. this.disconnect();
  169. }
  170. }
  171.  
  172. window.addEventListener('locationchange', function() {
  173. observer[location.href.indexOf('/answer/') === -1 ? 'start' : 'end']();
  174. })
  175. window._collapsedAnswerObserver = observer;
  176. }
  177. return window._collapsedAnswerObserver
  178. }
  179.  
  180.  
  181. // 默认收起回答
  182. function defaultCollapsedAnswer() {
  183. if (!menu_value('menu_defaultCollapsedAnswer')) return
  184. const observer = getCollapsedAnswerObserver();
  185. if (location.href.indexOf('/answer/') === -1) {
  186. observer.start();
  187. }
  188. }
  189.  
  190.  
  191. // 一键收起回答(全部)
  192. function collapsedAnswer() {
  193. if (!menu_value('menu_collapsedAnswer')) return
  194. //console.log('1111', document.querySelector('.CornerAnimayedFlex'))
  195. if (document.querySelector('.CornerAnimayedFlex') && !document.getElementById('collapsed-button')) {
  196. //console.log('2222')
  197. document.head.appendChild(document.createElement('style')).textContent = '.CornerButton{margin-bottom:8px !important;}.CornerButtons{bottom:45px !important;}';
  198. document.querySelector('.CornerAnimayedFlex').insertAdjacentHTML('afterBegin', '<button id="collapsed-button" data-tooltip="收起全部回答" data-tooltip-position="left" data-tooltip-will-hide-on-click="false" aria-label="收起全部回答" type="button" class="Button CornerButton Button--plain"><svg class="ContentItem-arrowIcon is-active" aria-label="收起全部回答" fill="currentColor" viewBox="0 0 24 24" width="24" height="24"><path d="M16.036 19.59a1 1 0 0 1-.997.995H9.032a.996.996 0 0 1-.997-.996v-7.005H5.03c-1.1 0-1.36-.633-.578-1.416L11.33 4.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.005z"></path></svg></button>');
  199. document.getElementById('collapsed-button').onclick = function () {
  200. if (location.pathname === '/' || location.pathname === '/hot' || location.pathname === '/follow') {
  201. document.querySelectorAll('.ContentItem-rightButton').forEach(function (el) {
  202. if (el.hasAttribute('data-zop-retract-question')) {
  203. el.click()
  204. }
  205. });
  206. } else {
  207. document.querySelectorAll('[script-collapsed]').forEach(function(scriptCollapsed) {
  208. scriptCollapsed.querySelectorAll('.ContentItem-actions [data-zop-retract-question], .ContentItem-actions.Sticky [data-zop-retract-question]').forEach(function(button) {
  209. button.click();
  210. })
  211. })
  212. document.querySelectorAll('.RichContent:not([script-collapsed]) .ContentItem-actions.Sticky [data-zop-retract-question]').forEach(function(button) {
  213. let el = button.parentElement;
  214. while (!el.classList.contains('RichContent')) {
  215. el = el.parentElement;
  216. }
  217. if (el) {
  218. el.setAttribute('script-collapsed', '');
  219. }
  220. button.click();
  221. })
  222. const observer = getCollapsedAnswerObserver();
  223. observer.start();
  224. if (!menu_value('menu_defaultCollapsedAnswer') && !observer._disconnectListener) {
  225. window.addEventListener('locationchange', function() {
  226. observer.end();
  227. window._collapsedAnswerObserver = null;
  228. })
  229. observer._disconnectListener = true;
  230. }
  231. }
  232. }
  233. }
  234. }
  235.  
  236.  
  237. // 收起当前回答、评论(监听点击事件,点击网页两侧空白处)
  238. function collapsedNowAnswer(selectors) {
  239. backToTop(selectors) // 快捷回到顶部
  240. if (!menu_value('menu_collapsedNowAnswer')) return
  241. document.querySelector(selectors).onclick = function(event){
  242. if (event.target == this) {
  243. // 下面这段主要是 [收起回答],顺便 [收起评论](如果展开了的话)
  244. let rightButton = document.querySelector('.ContentItem-actions.Sticky.RichContent-actions.is-fixed.is-bottom')
  245. // 悬浮在底部的 [收起回答](此时正在浏览回答内容 [中间区域])
  246. if (rightButton) {
  247. // 固定的 [收起评论](先看看是否展开评论)
  248. let commentCollapseButton = rightButton.querySelector('button.Button.ContentItem-action.Button--plain.Button--withIcon.Button--withLabel:first-of-type')
  249. //console.log('111')
  250. if (commentCollapseButton && commentCollapseButton.textContent.indexOf('收起评论') > -1) commentCollapseButton.click();
  251. // 再去收起回答
  252. rightButton = rightButton.querySelector('.ContentItem-rightButton[data-zop-retract-question]')
  253. //console.log('222')
  254. if (rightButton) rightButton.click();
  255. // 固定在回答底部的 [收起回答](此时正在浏览回答内容 [尾部区域])
  256. } else {
  257. let answerCollapseButton_ = false;
  258. for (let el of document.querySelectorAll('.ContentItem-rightButton[data-zop-retract-question]')) { // 遍历所有回答底部的 [收起] 按钮
  259. if (isElementInViewport(el)) { // 判断该 [收起] 按钮是否在可视区域内
  260. // 固定的 [收起评论](先看看是否展开评论,即存在 [收起评论] 按钮)
  261. let commentCollapseButton = el.parentNode.querySelector('button.Button.ContentItem-action.Button--plain.Button--withIcon.Button--withLabel:first-of-type')
  262. // 如果展开了评论,就收起评论
  263. //console.log('333')
  264. if (commentCollapseButton && commentCollapseButton.textContent.indexOf('收起评论') > -1) commentCollapseButton.click();
  265. //console.log('444')
  266. el.click() // 再去收起回答
  267. answerCollapseButton_ = true; // 如果找到并点击收起了,就没必要执行下面的代码了(可视区域中没有 [收起回答] 时)
  268. break
  269. }
  270. }
  271. // 针对完全看不到 [收起回答] 按钮时(如 [头部区域],以及部分明明很长却不显示悬浮横条的回答)
  272. if (!answerCollapseButton_) {
  273. for (let el of document.querySelectorAll('.List-item, .Card.AnswerCard')) { // 遍历所有回答主体元素
  274. if (isElementInViewport_(el)) { // 判断该回答是否在可视区域内
  275. // 固定的 [收起评论](先看看是否展开评论,即存在 [收起评论] 按钮)
  276. let commentCollapseButton = el.parentNode.querySelector('button.Button.ContentItem-action.Button--plain.Button--withIcon.Button--withLabel:first-of-type')
  277. // 如果展开了评论,就收起评论
  278. //console.log('555')
  279. if (commentCollapseButton && commentCollapseButton.textContent.indexOf('收起评论') > -1) commentCollapseButton.click();
  280. let answerCollapseButton__ = el.querySelector('.ContentItem-rightButton[data-zop-retract-question]');
  281. //console.log('666')
  282. if (answerCollapseButton__) answerCollapseButton__.click() // 再去收起回答
  283. break
  284. }
  285. }
  286. }
  287. }
  288.  
  289. // 下面这段只针对 [收起评论](如果展开了的话)
  290. let commentCollapseButton_ = false, commentCollapseButton__ = false;
  291. // 悬浮的 [收起评论](此时正在浏览评论内容 [中间区域])
  292. let commentCollapseButton = document.querySelector('.CommentCollapseButton')
  293. if (commentCollapseButton) {
  294. //console.log('777')
  295. commentCollapseButton.click();
  296. } else { // 固定的 [收起评论](此时正在浏览评论内容 [头部区域])
  297. let commentCollapseButton_1 = document.querySelectorAll('.ContentItem-actions > button.Button.ContentItem-action.Button--plain.Button--withIcon.Button--withLabel:first-of-type, .ContentItem-action > button.Button.Button--plain.Button--withIcon.Button--withLabel:first-of-type')
  298. if (commentCollapseButton_1.length > 0) {
  299. for (let el of commentCollapseButton_1) {
  300. if (el.textContent.indexOf('收起评论') > -1) {
  301. if (isElementInViewport(el)) {
  302. //console.log('888')
  303. el.click()
  304. commentCollapseButton_ = true // 如果找到并点击了,就没必要执行下面的代码了(可视区域中没有 [收起评论] 时)
  305. break
  306. }
  307. }
  308. }
  309. }
  310. if (commentCollapseButton_ == false) { // 可视区域中没有 [收起评论] 时(此时正在浏览评论内容 [头部区域] + [尾部区域](不上不下的,既看不到固定的 [收起评论] 又看不到悬浮的 [收起评论])),需要判断可视区域中是否存在评论元素
  311. let commentCollapseButton_1 = document.querySelectorAll('.NestComment')
  312. if (commentCollapseButton_1.length > 0) {
  313. for (let el of commentCollapseButton_1) {
  314. if (isElementInViewport(el)) {
  315. let commentCollapseButton = findParentElement(el, 'ContentItem AnswerItem').querySelector('.ContentItem-actions > button.Button.ContentItem-action.Button--plain.Button--withIcon.Button--withLabel:first-of-type')
  316. if (commentCollapseButton.textContent.indexOf('收起评论') > -1) {
  317. //console.log('999')
  318. commentCollapseButton.click()
  319. commentCollapseButton__ = true // 如果找到并点击了,就没必要执行下面的代码了(可视区域中没有 评论元素 时)
  320. break
  321. }
  322. }
  323. }
  324. }
  325. if (commentCollapseButton__ == false) { // 如果上面的都没找到,那么就尝试寻找评论末尾的 [评论回复框]
  326. let commentCollapseButton_2 = document.querySelectorAll('.CommentsV2-footer.CommentEditorV2--normal .CommentEditorV2-inputWrap')
  327. if (commentCollapseButton_2.length > 0) {
  328. for (let el of commentCollapseButton_2) {
  329. if (isElementInViewport(el)) {
  330. let commentCollapseButton = findParentElement(el, 'ContentItem AnswerItem').querySelector('.ContentItem-actions > button.Button.ContentItem-action.Button--plain.Button--withIcon.Button--withLabel:first-of-type')
  331. //console.log(commentCollapseButton)
  332. if (commentCollapseButton.textContent.indexOf('收起评论') > -1) {
  333. //console.log('101010')
  334. commentCollapseButton.click()
  335. break
  336. }
  337. }
  338. }
  339. }
  340. }
  341. }
  342. }
  343. }
  344. }
  345. }
  346.  
  347.  
  348. // 回到顶部(监听点击事件,鼠标右键点击网页两侧空白处)
  349. function backToTop(selectors) {
  350. if (!menu_value('menu_backToTop')) return
  351. document.querySelector(selectors).oncontextmenu = function(event){
  352. if (event.target == this) {
  353. event.preventDefault();
  354. window.scrollTo(0,0)
  355. }
  356. }
  357. }
  358.  
  359.  
  360. //获取元素是否在可视区域(完全可见)
  361. function isElementInViewport(el) {
  362. let rect = el.getBoundingClientRect();
  363. return (
  364. rect.top >= 0 &&
  365. rect.left >= 0 &&
  366. rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
  367. rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  368. );
  369. }
  370. //获取元素是否在可视区域(部分可见)
  371. function isElementInViewport_(el) {
  372. let rect = el.getBoundingClientRect();
  373. return (
  374. rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
  375. rect.left <= (window.innerWidth || document.documentElement.clientWidth)
  376. );
  377. }
  378.  
  379.  
  380. // 自定义屏蔽用户
  381. function customBlockUsers() {
  382. let nowBlockUsers = '';
  383. menu_value('menu_customBlockUsers').forEach(function(item){nowBlockUsers += '|' + item})
  384. //console.log(nowBlockUsers.replace('|',''))
  385. let newBlockUsers = prompt('编辑 [自定义屏蔽用户]\n(不同用户名之间使用 "|" 分隔,例如:用户A|用户B|用户C )', nowBlockUsers.replace('|',''));
  386. if (newBlockUsers === '') {
  387. GM_setValue('menu_customBlockUsers', []);
  388. registerMenuCommand(); // 重新注册脚本菜单
  389. } else if (newBlockUsers != null) {
  390. GM_setValue('menu_customBlockUsers', newBlockUsers.split('|'));
  391. registerMenuCommand(); // 重新注册脚本菜单
  392. }
  393. };
  394.  
  395.  
  396. // 屏蔽指定用户
  397. function blockUsers(type) {
  398. if (!menu_value('menu_blockUsers')) return
  399. if (!menu_value('menu_customBlockUsers') || menu_value('menu_customBlockUsers').length < 1) return
  400. switch(type) {
  401. case 'index':
  402. blockUsers_('.Card.TopstoryItem.TopstoryItem--old.TopstoryItem-isRecommend', 'Card TopstoryItem TopstoryItem--old TopstoryItem-isRecommend');
  403. break;
  404. case 'question':
  405. blockUsers_question();
  406. break;
  407. case 'search':
  408. blockUsers_search();
  409. break;
  410. case 'topic':
  411. blockUsers_('.List-item.TopicFeedItem', 'List-item TopicFeedItem');
  412. break;
  413. case 'people':
  414. blockUsers_button_people(); // 添加屏蔽用户按钮(用户主页)
  415. break;
  416. }
  417. blockUsers_comment(); // 评论区
  418. blockUsers_button(); // 加入黑名单按钮
  419.  
  420. function blockUsers_(className1, className2) {
  421. // 前几条因为是直接加载的,而不是动态插入网页的,所以需要单独判断
  422. function blockKeywords_now() {
  423. document.querySelectorAll(className1).forEach(function(item1){
  424. let item = item1.querySelector('.ContentItem.AnswerItem, .ContentItem.ArticleItem'); // 用户名所在元素
  425. if (item) {
  426. for (const keyword of menu_value('menu_customBlockUsers')) { // 遍历用户名黑名单
  427. if (item.dataset.zop.indexOf('authorName":"' + keyword + '",') > -1) { // 找到就删除该信息流
  428. console.log(item.dataset.zop);
  429. item1.hidden = true;
  430. break;
  431. }
  432. }
  433. }
  434. })
  435. }
  436.  
  437. blockKeywords_now();
  438. window.addEventListener('locationchange', function(){
  439. setTimeout(blockKeywords_now, 500); // 网页 URL 变化后再次执行
  440. })
  441.  
  442. // 这个是监听网页插入事件,用来判断后续网页动态插入的元素
  443. const callback = (mutationsList, observer) => {
  444. for (const mutation of mutationsList) {
  445. for (const target of mutation.addedNodes) {
  446. if (target.nodeType != 1) return
  447. if (target.className === className2) {
  448. let item = target.querySelector('.ContentItem.AnswerItem, .ContentItem.ArticleItem'); // 用户名所在元素
  449. if (item) {
  450. for (const keyword of menu_value('menu_customBlockUsers')) { // 遍历用户名黑名单
  451. if (item.dataset.zop.indexOf('authorName":"' + keyword + '",') > -1) { // 找到就删除该信息流
  452. console.log(item.dataset.zop);
  453. target.hidden = true;
  454. break;
  455. }
  456. }
  457. }
  458. }
  459. }
  460. }
  461. };
  462. const observer = new MutationObserver(callback);
  463. observer.observe(document, { childList: true, subtree: true });
  464. }
  465.  
  466.  
  467. function blockUsers_question() {
  468. const blockUsers_question_ = (mutationsList, observer) => {
  469. for (const mutation of mutationsList) {
  470. for (const target of mutation.addedNodes) {
  471. if (target.nodeType != 1) return
  472. if (target.className === 'List-item' || target.className === 'Card AnswerCard') {
  473. let item1 = target.querySelector('.ContentItem.AnswerItem');
  474. if (item1) {
  475. menu_value('menu_customBlockUsers').forEach(function(item2){ // 遍历用户黑名单
  476. if (item1.dataset.zop.indexOf('authorName":"' + item2 + '",') > -1) { // 找到就删除该回答
  477. console.log(item1.dataset.zop)
  478. target.hidden = true;
  479. }
  480. })
  481. }
  482. }
  483. }
  484. }
  485. };
  486.  
  487. const blockUsers_question_answer_ = (mutationsList, observer) => {
  488. for (const mutation of mutationsList) {
  489. for (const target of mutation.addedNodes) {
  490. if (target.nodeType != 1) return
  491. target.querySelectorAll('.List-item, .Card.AnswerCard').forEach(function(item){
  492. let item1 = item.querySelector('.ContentItem.AnswerItem');
  493. if (item1) {
  494. menu_value('menu_customBlockUsers').forEach(function(item2){ // 遍历用户黑名单
  495. if (item1.dataset.zop.indexOf('authorName":"' + item2 + '",') > -1) { // 找到就删除该回答
  496. console.log(item1.dataset.zop)
  497. item.hidden = true;
  498. }
  499. })
  500. }
  501. })
  502. }
  503. }
  504. };
  505.  
  506. if (location.pathname.indexOf('/answer/') > -1) { // 回答页(就是只有三个回答的页面)
  507. const observer = new MutationObserver(blockUsers_question_answer_);
  508. observer.observe(document, { childList: true, subtree: true });
  509. } else { // 问题页(可以显示所有回答的页面)
  510. const observer = new MutationObserver(blockUsers_question_);
  511. observer.observe(document, { childList: true, subtree: true });
  512. }
  513.  
  514. // 针对的是打开网页后直接加载的前面几个回答(上面哪些是针对动态加载的回答)
  515. document.querySelectorAll('.List-item, .Card.AnswerCard').forEach(function(item){
  516. let item1 = item.querySelector('.ContentItem.AnswerItem');
  517. if (item1) {
  518. menu_value('menu_customBlockUsers').forEach(function(item2){ // 遍历用户黑名单
  519. if (item1.dataset.zop.indexOf('authorName":"' + item2 + '",') > -1) { // 找到就删除该回答
  520. console.log(item1.dataset.zop)
  521. item.hidden = true;
  522. }
  523. })
  524. }
  525. })
  526. }
  527.  
  528. function blockUsers_search() {
  529. function blockUsers_now() {
  530. if (location.search.indexOf('type=content') === -1) return // 目前只支持搜索页的 [综合]
  531. document.querySelectorAll('.Card.SearchResult-Card[data-za-detail-view-path-module="AnswerItem"], .Card.SearchResult-Card[data-za-detail-view-path-module="PostItem"]').forEach(function(item1){
  532. let item = item1.querySelector('.RichText.ztext.CopyrightRichText-richText b'); // 标题所在元素
  533. if (item) {
  534. for (const keyword of menu_value('menu_customBlockUsers')) { // 遍历关键词黑名单
  535. if (item.textContent === keyword) { // 找到就删除该信息流
  536. console.log(item.textContent);
  537. item1.hidden = true;
  538. break;
  539. }
  540. }
  541. }
  542. })
  543. }
  544.  
  545. setTimeout(blockUsers_now, 500);
  546. window.addEventListener('locationchange', function(){
  547. setTimeout(blockUsers_now, 500); // 网页 URL 变化后再次执行
  548. })
  549.  
  550. const callback = (mutationsList, observer) => {
  551. if (location.search.indexOf('type=content') === -1) return // 目前只支持搜索页的 [综合]
  552. for (const mutation of mutationsList) {
  553. for (const target of mutation.addedNodes) {
  554. if (target.nodeType != 1) return
  555. if (target.className === 'Card SearchResult-Card' && (target.dataset.zaDetailViewPathModule === 'AnswerItem' || target.dataset.zaDetailViewPathModule === 'PostItem')) {
  556. let item = target.querySelector('.RichText.ztext.CopyrightRichText-richText b'); // 用户名所在元素
  557. if (item) {
  558. for (const keyword of menu_value('menu_customBlockUsers')) { // 遍历用户名黑名单
  559. if (item.textContent === keyword) { // 找到就删除该信息流
  560. console.log(item.textContent);
  561. target.hidden = true;
  562. break;
  563. }
  564. }
  565. }
  566. }
  567. }
  568. }
  569. };
  570. const observer = new MutationObserver(callback);
  571. observer.observe(document, { childList: true, subtree: true });
  572. }
  573.  
  574. function blockUsers_comment() {
  575. const callback = (mutationsList, observer) => {
  576. for (const mutation of mutationsList) {
  577. for (const target of mutation.addedNodes) {
  578. if (target.nodeType != 1) return
  579. let item = target.querySelector('img.Avatar.UserLink-avatar')
  580. if (item) {
  581. menu_value('menu_customBlockUsers').forEach(function(item1){ // 遍历用户黑名单
  582. if (item.alt === item1) { // 找到就删除该搜索结果
  583. if (findParentElement(item, 'NestComment--rootComment', true)) {
  584. findParentElement(item, 'NestComment--rootComment', true).hidden = true;;
  585. } else if (findParentElement(item, 'NestComment--child', true)){
  586. findParentElement(item, 'NestComment--child', true).hidden = true;;
  587. } else if (findParentElement(item, 'NestComment', true)){
  588. findParentElement(item, 'NestComment', true).hidden = true;;
  589. } else if (findParentElement(item, 'CommentItemV2', true)){
  590. findParentElement(item, 'CommentItemV2', true).hidden = true;;
  591. } else if (findParentElement(item, 'CommentItemV2 CommentItemV2--highlighted', true)){
  592. findParentElement(item, 'CommentItemV2 CommentItemV2--highlighted', true).hidden = true;;
  593. }
  594. }
  595. })
  596.  
  597. // 添加屏蔽用户按钮(点赞、回复等按钮后面)
  598. if (item) {
  599. let footer = findParentElement(item, 'CommentItemV2-meta', true).parentElement.querySelector('.CommentItemV2-metaSibling > .CommentItemV2-footer'),
  600. userid = item.parentElement;
  601. if (userid && footer && !footer.lastElementChild.dataset.name) {
  602. userid = userid.href.split('/')[4];
  603. footer.insertAdjacentHTML('beforeend',`<button type="button" data-name="${item.alt}" data-userid="${userid}" class="Button CommentItemV2-hoverBtn Button--plain"><span style="display: inline-flex; align-items: center;">&#8203;<svg class="Zi Zi--Like" fill="currentColor" viewBox="0 0 24 24" width="16" height="16" style="transform: rotate(180deg); margin-right: 5px;"><path d="M18.376 5.624c-3.498-3.499-9.254-3.499-12.752 0-3.499 3.498-3.499 9.254 0 12.752 3.498 3.499 9.254 3.499 12.752 0 3.499-3.498 3.499-9.14 0-12.752zm-1.693 1.693c2.37 2.37 2.596 6.094.678 8.69l-9.367-9.48c2.708-1.919 6.32-1.58 8.69.79zm-9.48 9.48c-2.37-2.37-2.595-6.095-.676-8.69l9.48 9.48c-2.822 1.918-6.433 1.58-8.803-.79z" fill-rule="evenodd"></path></svg></span>屏蔽用户</button>`);
  604. footer.lastElementChild.onclick = function(){blockUsers_button_add(this.dataset.name, this.dataset.userid, false)}
  605. }
  606. }
  607. }
  608. }
  609. }
  610. };
  611. const observer = new MutationObserver(callback);
  612. observer.observe(document, { childList: true, subtree: true });
  613. }
  614.  
  615. // 添加屏蔽用户按钮(用户信息悬浮框中)
  616. function blockUsers_button() {
  617. const callback = (mutationsList, observer) => {
  618. for (const mutation of mutationsList) {
  619. for (const target of mutation.addedNodes) {
  620. if (target.nodeType != 1) return
  621. //console.log(target, target.className)
  622. if (target.className && (target.className.indexOf('Popover-content Popover-content--top HoverCard-popoverTarget') > -1 || target.className.indexOf('Popover-content Popover-content--bottom HoverCard-popoverTarget') > -1) || target.querySelector('.Popover-content.Popover-content--top.HoverCard-popoverTarget') || target.querySelector('.Popover-content.Popover-content--bottom.HoverCard-popoverTarget')) {
  623. let item = target.querySelector('.MemberButtonGroup.ProfileButtonGroup.HoverCard-buttons'),
  624. item1 = target.querySelector('a.UserLink-link'),
  625. name = item1.textContent,
  626. userid = item1.href.split('/')[4];
  627. if (item && !target.querySelector('button[data-name][data-userid]')) {
  628. item.insertAdjacentHTML('beforeend', `<button type="button" data-name="${name}" data-userid="${userid}" class="Button FollowButton Button--primary Button--red" style="width: 100%;margin: 7px 0 0 0;"><span style="display: inline-flex; align-items: center;">​<svg class="Zi Zi--Plus FollowButton-icon" fill="currentColor" viewBox="0 0 24 24" width="1.2em" height="1.2em"><path d="M18.376 5.624c-3.498-3.499-9.254-3.499-12.752 0-3.499 3.498-3.499 9.254 0 12.752 3.498 3.499 9.254 3.499 12.752 0 3.499-3.498 3.499-9.14 0-12.752zm-1.693 1.693c2.37 2.37 2.596 6.094.678 8.69l-9.367-9.48c2.708-1.919 6.32-1.58 8.69.79zm-9.48 9.48c-2.37-2.37-2.595-6.095-.676-8.69l9.48 9.48c-2.822 1.918-6.433 1.58-8.803-.79z" fill-rule="evenodd"></path></svg></span>屏蔽用户</button>`);
  629. item.lastElementChild.onclick = function(){blockUsers_button_add(this.dataset.name, this.dataset.userid, false)}
  630. }
  631. }
  632. }
  633. }
  634. };
  635. const observer = new MutationObserver(callback);
  636. observer.observe(document, { childList: true, subtree: true });
  637. }
  638.  
  639. // 添加屏蔽用户按钮(用户主页)
  640. function blockUsers_button_people() {
  641. let item = document.querySelector('.MemberButtonGroup.ProfileButtonGroup.ProfileHeader-buttons'), // 获取按钮元素位置
  642. name = document.querySelector('.ProfileHeader-name').firstChild.textContent, // 获取用户名
  643. users = menu_value('menu_customBlockUsers'), // 读取屏蔽列表
  644. userid = location.href.split('/')[4];
  645. for (let num = 0;num<users.length;num++) { // 判断是否已存在
  646. if (users[num] === name) { // 已存在
  647. document.querySelectorAll('.Button.Button--primary.Button--red').forEach(function(item){item.style.display = 'none';}) // 隐藏知乎自带的已屏蔽按钮
  648. item.insertAdjacentHTML('beforeend', `<button type="button" data-name="${name}" data-userid="${userid}" class="Button FollowButton Button--primary Button--red" style="margin: 0 0 0 12px;"><span style="display: inline-flex; align-items: center;">​<svg class="Zi Zi--Plus FollowButton-icon" fill="currentColor" viewBox="0 0 24 24" width="1.2em" height="1.2em"><path d="M18.376 5.624c-3.498-3.499-9.254-3.499-12.752 0-3.499 3.498-3.499 9.254 0 12.752 3.498 3.499 9.254 3.499 12.752 0 3.499-3.498 3.499-9.14 0-12.752zm-1.693 1.693c2.37 2.37 2.596 6.094.678 8.69l-9.367-9.48c2.708-1.919 6.32-1.58 8.69.79zm-9.48 9.48c-2.37-2.37-2.595-6.095-.676-8.69l9.48 9.48c-2.822 1.918-6.433 1.58-8.803-.79z" fill-rule="evenodd"></path></svg></span>取消屏蔽</button>`);
  649. item.lastElementChild.onclick = function(){blockUsers_button_del(this.dataset.name, this.dataset.userid, true)}
  650. return
  651. }
  652. };
  653. if (item) {
  654. item.insertAdjacentHTML('beforeend', `<button type="button" data-name="${name}" data-userid="${userid}" class="Button FollowButton Button--primary Button--red" style="margin: 0 0 0 12px;"><span style="display: inline-flex; align-items: center;">​<svg class="Zi Zi--Plus FollowButton-icon" fill="currentColor" viewBox="0 0 24 24" width="1.2em" height="1.2em"><path d="M18.376 5.624c-3.498-3.499-9.254-3.499-12.752 0-3.499 3.498-3.499 9.254 0 12.752 3.498 3.499 9.254 3.499 12.752 0 3.499-3.498 3.499-9.14 0-12.752zm-1.693 1.693c2.37 2.37 2.596 6.094.678 8.69l-9.367-9.48c2.708-1.919 6.32-1.58 8.69.79zm-9.48 9.48c-2.37-2.37-2.595-6.095-.676-8.69l9.48 9.48c-2.822 1.918-6.433 1.58-8.803-.79z" fill-rule="evenodd"></path></svg></span>屏蔽用户</button>`);
  655. item.lastElementChild.onclick = function(){blockUsers_button_add(this.dataset.name, this.dataset.userid, true)}
  656. }
  657. }
  658.  
  659. // 屏蔽用户按钮绑定事件(添加)
  660. function blockUsers_button_add(name, userid, reload) {
  661. if (!name || !userid) return
  662. let users = menu_value('menu_customBlockUsers'), // 读取屏蔽列表
  663. index = users.indexOf(name);
  664. if (index === -1) {
  665. users.push(name); // 追加用户名
  666. GM_setValue('menu_customBlockUsers', users); // 写入屏蔽列表
  667. // 加入知乎自带的黑名单(和本脚本互补~
  668. GM_xmlhttpRequest({url: `https://www.zhihu.com/api/v4/members/${userid}/actions/block`,method: 'POST',timeout: 2000});
  669. // 是否刷新本页
  670. if (reload) {
  671. setTimeout(function(){location.reload()}, 200); // 刷新网页,延迟 200 毫秒,避免知乎反应慢~
  672. } else {
  673. GM_notification({text: `该用户已被屏蔽~\n刷新网页后生效~`, timeout: 3000});
  674. }
  675. } else {
  676. GM_notification({text: `该用户已经被屏蔽啦,无需重复屏蔽~`, timeout: 3000});
  677. }
  678. }
  679.  
  680.  
  681. // 屏蔽用户按钮绑定事件(删除)
  682. function blockUsers_button_del(name, userid, reload) {
  683. if (!name || !userid) return
  684. let users = menu_value('menu_customBlockUsers'), // 读取屏蔽列表
  685. index = users.indexOf(name);
  686. if (index > -1) {
  687. users.splice(index, 1); // 移除用户名
  688. GM_setValue('menu_customBlockUsers', users); // 写入屏蔽列表
  689. // 移除知乎自带的黑名单
  690. GM_xmlhttpRequest({url: `https://www.zhihu.com/api/v4/members/${userid}/actions/block`,method: 'DELETE',timeout: 2000});
  691. // 是否刷新本页
  692. if (reload) {
  693. setTimeout(function(){location.reload()}, 200); // 刷新网页,延迟 200 毫秒,避免知乎反应慢~
  694. } else {
  695. GM_notification({text: `该用户已取消屏蔽啦~\n刷新网页后生效~`, timeout: 3000});
  696. }
  697. } else {
  698. GM_notification({text: `没有在屏蔽列表中找到该用户...`, timeout: 3000});
  699. }
  700. }
  701. }
  702.  
  703.  
  704. // 自定义屏蔽关键词(标题)
  705. function customBlockKeywords() {
  706. let nowBlockKeywords = '';
  707. menu_value('menu_customBlockKeywords').forEach(function(item){nowBlockKeywords += '|' + item})
  708. let newBlockKeywords = prompt('编辑 [自定义屏蔽关键词]\n(不同关键词之间使用 "|" 分隔,例如:关键词A|关键词B|关键词C \n(关键词不区分大小写,支持表情如:[捂脸]|[飙泪笑]', nowBlockKeywords.replace('|',''));
  709. if (newBlockKeywords === '') {
  710. GM_setValue('menu_customBlockKeywords', []);
  711. registerMenuCommand(); // 重新注册脚本菜单
  712. } else if (newBlockKeywords != null) {
  713. GM_setValue('menu_customBlockKeywords', newBlockKeywords.split('|'));
  714. registerMenuCommand(); // 重新注册脚本菜单
  715. }
  716. };
  717.  
  718.  
  719. // 屏蔽指定关键词
  720. function blockKeywords(type) {
  721. if (!menu_value('menu_blockKeywords')) return
  722. if (!menu_value('menu_customBlockKeywords') || menu_value('menu_customBlockKeywords').length < 1) return
  723. switch(type) {
  724. case 'index':
  725. blockKeywords_('.Card.TopstoryItem.TopstoryItem--old.TopstoryItem-isRecommend', 'Card TopstoryItem TopstoryItem--old TopstoryItem-isRecommend');
  726. break;
  727. case 'topic':
  728. blockKeywords_('.List-item.TopicFeedItem', 'List-item TopicFeedItem');
  729. break;
  730. case 'people':
  731. blockKeywords_('.List-item', 'List-item');
  732. break;
  733. case 'collection':
  734. blockKeywords_('.Card.CollectionDetailPageItem', 'Card CollectionDetailPageItem');
  735. break;
  736. case 'search':
  737. blockKeywords_search();
  738. break;
  739. case 'comment':
  740. blockKeywords_comment();
  741. break;
  742. }
  743.  
  744.  
  745. function blockKeywords_(className1, className2) {
  746. // 前几条因为是直接加载的,而不是动态插入网页的,所以需要单独判断
  747. function blockKeywords_now() {
  748. if (location.pathname === '/hot') {
  749. document.querySelectorAll('.HotItem').forEach(function(item1){blockKeywords_1(item1, 'h2.HotItem-title');})
  750. } else {
  751. document.querySelectorAll(className1).forEach(function(item1){blockKeywords_1(item1, 'h2.ContentItem-title meta[itemprop="name"], meta[itemprop="headline"]');})
  752. }
  753. }
  754.  
  755. blockKeywords_now();
  756. window.addEventListener('locationchange', function(){
  757. setTimeout(blockKeywords_now, 500); // 网页 URL 变化后再次执行
  758. })
  759.  
  760. // 这个是监听网页插入事件,用来判断后续网页动态插入的元素
  761. const callback = (mutationsList, observer) => {
  762. for (const mutation of mutationsList) {
  763. for (const target of mutation.addedNodes) {
  764. if (target.nodeType != 1) return
  765. if (target.className === className2) {blockKeywords_1(target, 'h2.ContentItem-title meta[itemprop="name"], meta[itemprop="headline"]');}
  766. }
  767. }
  768. };
  769. const observer = new MutationObserver(callback);
  770. observer.observe(document, { childList: true, subtree: true });
  771. }
  772.  
  773.  
  774. function blockKeywords_search() {
  775. function blockKeywords_now() {
  776. if (location.search.indexOf('type=content') === -1) return // 目前只支持搜索页的 [综合]
  777. document.querySelectorAll('.HotLanding-contentItem, .Card.SearchResult-Card[data-za-detail-view-path-module="AnswerItem"], .Card.SearchResult-Card[data-za-detail-view-path-module="PostItem"]').forEach(function(item1){blockKeywords_1(item1, 'a[data-za-detail-view-id]');})
  778. }
  779.  
  780. setTimeout(blockKeywords_now, 500);
  781. window.addEventListener('locationchange', function(){
  782. setTimeout(blockKeywords_now, 500); // 网页 URL 变化后再次执行
  783. })
  784.  
  785. const callback = (mutationsList, observer) => {
  786. if (location.search.indexOf('type=content') === -1) return // 目前只支持搜索页的 [综合]
  787. for (const mutation of mutationsList) {
  788. for (const target of mutation.addedNodes) {
  789. if (target.nodeType != 1) return
  790. if (target.className === 'Card SearchResult-Card' && (target.dataset.zaDetailViewPathModule === 'AnswerItem' || target.dataset.zaDetailViewPathModule === 'PostItem')) {blockKeywords_1(target, 'a[data-za-detail-view-id]');}
  791. }
  792. }
  793. };
  794. const observer = new MutationObserver(callback);
  795. observer.observe(document, { childList: true, subtree: true });
  796. }
  797.  
  798.  
  799. function blockKeywords_comment() {
  800. function filterComment(comment) {
  801. let content = comment.querySelector('.RichText'); // 寻找评论文字所在元素
  802. let texts = [content.textContent.toLowerCase()]; // 因为要针对评论中的表情,所以需要整个数组并全部转为小写(用来不区分大小写)
  803. for (let i = 0; i < content.children.length; i++) { // 该条针对的是评论中的表情
  804. let emoticonValue = content.children[i].getAttribute('data-zhihu-emoticon'); // 确定是表情就将其添加到稍后遍历的数组中
  805. if (emoticonValue) {
  806. texts.push(emoticonValue)
  807. }
  808. }
  809.  
  810. let keywords = menu_value('menu_customBlockKeywords');
  811. for (const text of texts) {
  812. for (const keyword of keywords) { // 遍历关键词黑名单
  813. if (text.indexOf(keyword.toLowerCase()) > -1) { // 找到就删除该评论
  814. console.log(text);
  815. content.textContent = '[该评论已屏蔽]';
  816. break;
  817. }
  818. }
  819. }
  820. }
  821.  
  822. const callback = (mutationsList, observer) => {
  823. for (const mutation of mutationsList) {
  824. for (const target of mutation.addedNodes) {
  825. if (target.nodeType != 1) return
  826. for (const node of target.querySelectorAll('*')) {
  827. if (node.className === 'CommentItemV2-metaSibling') filterComment(node);
  828. }
  829. }
  830. }
  831. };
  832. const observer = new MutationObserver(callback);
  833. observer.observe(document, { childList: true, subtree: true });
  834. }
  835.  
  836. function blockKeywords_1(item1, css) {
  837. let item = item1.querySelector(css); // 标题所在元素
  838. if (item) {
  839. for (const keyword of menu_value('menu_customBlockKeywords')) { // 遍历关键词黑名单
  840. let text = item.content || item.textContent;
  841. if (text.toLowerCase().indexOf(keyword.toLowerCase()) > -1) { // 找到就删除该信息流
  842. console.log(text);
  843. item1.hidden = true;
  844. item1.style.display = 'none';
  845. break;
  846. }
  847. }
  848. }
  849. }
  850. }
  851.  
  852.  
  853. // 屏蔽指定类别(视频/文章等)
  854. function blockType(type) {
  855. let name;
  856. // 一开始加载的信息流 + 添加标签样式
  857. if (type === 'search') { // 搜索页
  858. if (!menu_value('menu_blockTypeVideo') && !menu_value('menu_blockTypeArticle') && !menu_value('menu_blockTypeTopic') && !menu_value('menu_blockTypeSearch')) return
  859. if (menu_value('menu_blockTypeSearch') && location.pathname === '/search') setTimeout(function(){document.querySelector('.RelevantQuery').parentElement.parentElement.hidden = true;;}, 1000)
  860. name = 'h2.ContentItem-title a:not(.zhihu_e_toQuestion), a.KfeCollection-PcCollegeCard-link, h2.SearchTopicHeader-Title a'
  861. addSetInterval_(name);
  862. } else if (type === 'question') { // 问题页
  863. if (!menu_value('menu_blockTypeVideo')) return
  864. document.lastChild.appendChild(document.createElement('style')).textContent = `.VideoAnswerPlayer, .VideoAnswerPlayer-video, .VideoAnswerPlayer-iframe {display: none !important;}`;
  865. name = '.VideoAnswerPlayer'
  866. document.querySelectorAll(name).forEach(function(item){blockType_(item);})
  867. } else { // 首页
  868. if (!menu_value('menu_blockTypeVideo') && !menu_value('menu_blockTypeArticle')) return
  869. if (menu_value('menu_blockTypeVideo')) document.lastChild.appendChild(document.createElement('style')).textContent = `.Card .ZVideoItem-video, nav.TopstoryTabs > a[aria-controls="Topstory-zvideo"] {display: none !important;}`;
  870. name = 'h2.ContentItem-title a:not(.zhihu_e_toQuestion)'
  871. document.querySelectorAll(name).forEach(function(item){blockType_(item);})
  872. }
  873.  
  874. // 后续加载的信息流
  875. const observer = new MutationObserver(mutationsList => {
  876. for (const mutation of mutationsList) {
  877. for (const target of mutation.addedNodes) {
  878. if (target.nodeType != 1) return
  879. blockType_(target.querySelector(name));
  880. }
  881. }
  882. });
  883. observer.observe(document, { childList: true, subtree: true });
  884.  
  885. window.addEventListener('locationchange', function(){
  886. addSetInterval_(name);
  887. // 移除相关搜索
  888. if (menu_value('menu_blockTypeSearch') && location.pathname === '/search' && location.search.indexOf('type=content') > -1) setTimeout(function(){document.querySelector('.RelevantQuery').parentElement.parentElement.hidden = true;}, 1500)
  889. })
  890.  
  891. function blockType_(titleA) {
  892. if (!titleA) return // 判断是否为真
  893. //console.log(titleA.href)
  894. if (location.pathname === '/search') { // 搜索页
  895. if (location.search.indexOf('type=content') === -1) return // 仅限搜索页的 [综合]
  896. if (titleA.href.indexOf('/zvideo/') > -1 || titleA.href.indexOf('video.zhihu.com') > -1) { // 如果是视频
  897. if (menu_value('menu_blockTypeVideo')) findParentElement(titleA, 'Card').hidden = true;
  898. } else if (titleA.href.indexOf('zhuanlan.zhihu.com') > -1) { // 如果是文章
  899. if (menu_value('menu_blockTypeArticle')) findParentElement(titleA, 'Card SearchResult-Card').hidden = true;
  900. } else if (titleA.href.indexOf('/topic/') > -1) { // 如果是话题
  901. if (menu_value('menu_blockTypeTopic')) findParentElement(titleA, 'Card SearchResult-Card').hidden = true;
  902. } else if (titleA.href.indexOf('/market/') > -1) { // 如果是杂志文章等乱七八糟的
  903. if (menu_value('menu_blockTypeArticle')) findParentElement(titleA, 'Card SearchResult-Card').hidden = true;
  904. }
  905. } else if (location.pathname.indexOf('/question/') > -1) { // 问题页
  906. if (menu_value('menu_blockTypeVideo')) findParentElement(titleA, 'List-item').hidden = true;
  907. } else { // 首页
  908. if (titleA.href.indexOf('/zvideo/') > -1 || titleA.href.indexOf('video.zhihu.com') > -1) { // 如果是视频
  909. if (menu_value('menu_blockTypeVideo')) findParentElement(titleA, 'Card TopstoryItem TopstoryItem-isRecommend').hidden = true;
  910. } else if (titleA.href.indexOf('/answer/') > -1) { // 如果是问题(视频回答)
  911. if (findParentElement(titleA, 'ContentItem AnswerItem').querySelector('.VideoAnswerPlayer')) {
  912. if (menu_value('menu_blockTypeVideo')) findParentElement(titleA, 'Card TopstoryItem TopstoryItem-isRecommend').hidden = true;
  913. }
  914. } else if (titleA.href.indexOf('zhuanlan.zhihu.com') > -1) { // 如果是文章
  915. if (menu_value('menu_blockTypeArticle')) findParentElement(titleA, 'Card TopstoryItem TopstoryItem-isRecommend').hidden = true;
  916. }
  917. }
  918. }
  919.  
  920. function addSetInterval_(A) {
  921. let timer = setInterval(function(){
  922. let aTag = document.querySelectorAll(A);
  923. if (aTag.length > 0) {
  924. clearInterval(timer);
  925. aTag.forEach(function(item){blockType_(item);})
  926. }
  927. });
  928. }
  929. }
  930.  
  931.  
  932. // 寻找父元素
  933. function findParentElement(item, className, type = false) {
  934. if (item.parentElement) {
  935. //console.log(item.parentElement)
  936. if (type) { // true = 完全一致,false = 包含即可
  937. if (item.parentElement.className && item.parentElement.className === className) {
  938. //console.log(item.parentElement.className)
  939. return item.parentElement;
  940. } else {
  941. let temp = findParentElement(item.parentElement, className, true)
  942. if (temp) return temp
  943. }
  944. } else {
  945. if (item.parentElement.className && item.parentElement.className.indexOf(className) > -1) {
  946. return item.parentElement;
  947. } else {
  948. let temp = findParentElement(item.parentElement, className)
  949. if (temp) return temp
  950. }
  951. }
  952. }
  953. return
  954. }
  955.  
  956.  
  957. // 移除高亮链接
  958. function removeHighlightLink() {
  959. const callback = (mutationsList, observer) => {
  960. for (const mutation of mutationsList) {
  961. for (const target of mutation.addedNodes) {
  962. if (target.nodeType != 1 || target.tagName != 'A') break
  963. if (target.dataset.zaNotTrackLink && target.href.indexOf('https://www.zhihu.com/search?q=') > -1) {
  964. target.parentElement.replaceWith(target.textContent);
  965. }
  966. }
  967. }
  968. };
  969. const observer = new MutationObserver(callback);
  970. observer.observe(document, { childList: true, subtree: true });
  971.  
  972. // 针对的是打开网页后直接加载的前面几个回答(上面哪些是针对动态加载的回答)
  973. document.querySelectorAll('span > a[data-za-not-track-link][href^="https://www.zhihu.com/search?q="]').forEach(e => e.parentElement.replaceWith(e.textContent))
  974. }
  975.  
  976.  
  977. // 屏蔽盐选内容
  978. function blockYanXuan() {
  979. if (!menu_value('menu_blockYanXuan')) return
  980. const blockYanXuan_question = (mutationsList, observer) => {
  981. for (const mutation of mutationsList) {
  982. for (const target of mutation.addedNodes) {
  983. if (target.nodeType != 1) return
  984. if (target.className === 'List-item' || target.className === 'Card AnswerCard') {
  985. if (target.querySelector('.KfeCollection-AnswerTopCard-Container, .KfeCollection-PurchaseBtn')) {
  986. target.hidden = true;
  987. }
  988. }
  989. }
  990. }
  991. };
  992.  
  993. const blockYanXuan_question_answer = (mutationsList, observer) => {
  994. for (const mutation of mutationsList) {
  995. for (const target of mutation.addedNodes) {
  996. if (target.nodeType != 1) return
  997. target.querySelectorAll('.List-item, .Card.AnswerCard').forEach(function(item){
  998. if (item.querySelector('.KfeCollection-AnswerTopCard-Container, .KfeCollection-PurchaseBtn')) {
  999. item.hidden = true;
  1000. }
  1001. })
  1002. }
  1003. }
  1004. };
  1005.  
  1006. if (location.pathname.indexOf('/answer/') > -1) { // 回答页(就是只有三个回答的页面)
  1007. const observer = new MutationObserver(blockYanXuan_question_answer);
  1008. observer.observe(document, { childList: true, subtree: true });
  1009. } else { // 问题页(可以显示所有回答的页面)
  1010. const observer = new MutationObserver(blockYanXuan_question);
  1011. observer.observe(document, { childList: true, subtree: true });
  1012. }
  1013.  
  1014. // 针对的是打开网页后直接加载的前面几个回答(上面哪些是针对动态加载的回答)
  1015. document.querySelectorAll('.List-item, .Card.AnswerCard').forEach(function(item){
  1016. if (item.querySelector('.KfeCollection-AnswerTopCard-Container, .KfeCollection-PurchaseBtn')) {
  1017. item.hidden = true;
  1018. }
  1019. })
  1020. }
  1021.  
  1022.  
  1023. // 区分问题文章
  1024. function addTypeTips() {
  1025. if (!menu_value('menu_typeTips')) return
  1026. let style = `font-weight: bold;font-size: 13px;padding: 1px 4px 0;border-radius: 2px;display: inline-block;vertical-align: top;margin: ${(location.pathname === '/search') ? '2' : '4'}px 4px 0 0;`
  1027. document.body.appendChild(document.createElement('style')).textContent = `/* 区分问题文章 */
  1028. .AnswerItem .ContentItem-title a:not(.zhihu_e_toQuestion)::before {content:'问题';color: #f68b83;background-color: #f68b8333;${style}}
  1029. .TopstoryQuestionAskItem .ContentItem-title a:not(.zhihu_e_toQuestion)::before {content:'问题';color: #ff5a4e;background-color: #ff5a4e33;${style}}
  1030. .ZVideoItem .ContentItem-title a::before, .ZvideoItem .ContentItem-title a::before {content:'视频';color: #00BCD4;background-color: #00BCD433;${style}}
  1031. .ArticleItem .ContentItem-title a::before {content:'文章';color: #2196F3;background-color: #2196F333;${style}}`;
  1032. }
  1033.  
  1034.  
  1035. // 直达问题按钮
  1036. function addToQuestion() {
  1037. if (!menu_value('menu_toQuestion')) return
  1038.  
  1039. // 一开始加载的信息流 + 添加按钮样式
  1040. if (location.pathname === '/search') {
  1041. document.lastChild.appendChild(document.createElement('style')).textContent = `a.zhihu_e_toQuestion {font-size: 13px !important;font-weight: normal !important;padding: 1px 6px 0 !important;border-radius: 2px !important;display: inline-block !important;vertical-align: top !important;height: 20.67px !important;line-height: 20.67px !important;margin-top: 2px !important;}`;
  1042. addSetInterval_('h2.ContentItem-title a:not(.zhihu_e_tips)');
  1043. } else {
  1044. document.lastChild.appendChild(document.createElement('style')).textContent = `a.zhihu_e_toQuestion {font-size: 13px !important;font-weight: normal !important;padding: 1px 6px 0 !important;border-radius: 2px !important;display: inline-block !important;vertical-align: top !important;margin-top: 4px !important;}`;
  1045. document.querySelectorAll('h2.ContentItem-title a:not(.zhihu_e_tips)').forEach(function(item){addTypeTips_(item);})
  1046. }
  1047.  
  1048. // 后续加载的信息流
  1049. const observer = new MutationObserver(mutationsList => {
  1050. for (const mutation of mutationsList) {
  1051. for (const target of mutation.addedNodes) {
  1052. if (target.nodeType != 1) return
  1053. addTypeTips_(target.querySelector('h2.ContentItem-title a:not(.zhihu_e_tips)'));
  1054. }
  1055. }
  1056. });
  1057. observer.observe(document, { childList: true, subtree: true });
  1058.  
  1059. window.addEventListener('locationchange', function(){
  1060. addSetInterval_('h2.ContentItem-title a:not(.zhihu_e_tips)');
  1061. })
  1062.  
  1063. function addTypeTips_(titleA) {
  1064. if (!titleA) return // 判断是否为真
  1065. if (titleA.parentElement.querySelector('a.zhihu_e_toQuestion')) return // 判断是否已添加
  1066. if (titleA.textContent.indexOf('?') != -1) { // 把问题末尾英文问好 [?] 的替换为中文问好 [?],这样按钮与标题之间的间距就刚刚好~
  1067. titleA.innerHTML = titleA.innerHTML.replace('?', "?")
  1068. }
  1069. if (/answer\/\d+/.test(titleA.href)) { // 如果是指向回答的问题(而非指向纯问题的链接)
  1070. titleA.insertAdjacentHTML('afterend', `<a class="zhihu_e_toQuestion VoteButton" href="${titleA.parentElement.querySelector('meta[itemprop="url"]').content}" target="_blank">直达问题</a>`);
  1071. }
  1072. }
  1073.  
  1074. function addSetInterval_(A) {
  1075. let timer = setInterval(function(){
  1076. let aTag = document.querySelectorAll(A);
  1077. if (aTag.length > 0) {
  1078. clearInterval(timer);
  1079. aTag.forEach(function(item){addTypeTips_(item);})
  1080. }
  1081. });
  1082. }
  1083. }
  1084.  
  1085.  
  1086. // 展开问题描述
  1087. function questionRichTextMore() {
  1088. if (!menu_value('menu_questionRichTextMore')) return
  1089. let button = document.querySelector('button.QuestionRichText-more');
  1090. if (button) button.click()
  1091. }
  1092.  
  1093.  
  1094. // 知乎免登录
  1095. function removeLogin() {
  1096. const removeLoginModal = (mutationsList, observer) => {
  1097. for (const mutation of mutationsList) {
  1098. for (const target of mutation.addedNodes) {
  1099. if (target.nodeType != 1) return
  1100. if (target.querySelector('.signFlowModal')) {
  1101. let button = target.querySelector('.Button.Modal-closeButton.Button--plain');
  1102. if (button) button.click();
  1103. }
  1104. }
  1105. }
  1106. };
  1107.  
  1108. // 未登录时才会监听并移除登录弹窗
  1109. if(location.hostname === 'zhuanlan.zhihu.com') { // 如果是文章页
  1110. if (!document.querySelector('button.ColumnPageHeader-MenuToggler')) { // 未登录
  1111. const observer = new MutationObserver(removeLoginModal);
  1112. observer.observe(document, { childList: true, subtree: true });
  1113. } else {
  1114. cleanTitles(); // 净化标题消息
  1115. }
  1116. } else { // 不是文章页
  1117. if (document.querySelector('button.AppHeader-login')) { // 未登录
  1118. const observer = new MutationObserver(removeLoginModal);
  1119. observer.observe(document, { childList: true, subtree: true });
  1120. document.lastElementChild.appendChild(document.createElement('style')).textContent = '.Question-mainColumnLogin, button.AppHeader-login {display: none !important;}'; // 屏蔽问题页中间的登录提示
  1121. document.querySelector('button.AppHeader-login').insertAdjacentHTML('afterend', '<a class="Button AppHeader-login Button--blue" href="https://www.zhihu.com/signin" target="_blank">登录</a>'); // [登录] 按钮跳转至登录页面
  1122. } else {
  1123. cleanTitles(); // 净化标题消息
  1124. }
  1125. }
  1126. }
  1127.  
  1128.  
  1129. // 净化标题消息
  1130. function cleanTitles() {
  1131. if (!menu_value('menu_cleanTitles')) return
  1132.  
  1133. // 方案一
  1134. const elTitle = document.head.querySelector('title');
  1135. const original = elTitle.textContent;
  1136. const observer = new MutationObserver(function() {
  1137. if (elTitle.textContent != original) { // 避免重复执行
  1138. elTitle.textContent = original;
  1139. }
  1140. });
  1141. observer.observe(elTitle, { childList: true });
  1142.  
  1143. // 方案二
  1144. // if (Reflect.getOwnPropertyDescriptor(document, 'title')) {
  1145. // const elTitle = document.head.querySelector('title');
  1146. // const original = elTitle.textContent;
  1147. // const observer = new MutationObserver(function() {
  1148. // if (elTitle.textContent != original) { // 避免重复执行
  1149. // elTitle.textContent = original;
  1150. // }
  1151. // });
  1152. // observer.observe(elTitle, { childList: true });
  1153. // } else {
  1154. // const title = document.title;
  1155. // Reflect.defineProperty(document, 'title', {
  1156. // set: () => {},
  1157. // get: () => title,
  1158. // });
  1159. // }
  1160. }
  1161.  
  1162.  
  1163. // 快捷关闭悬浮评论(监听点击事件,点击网页两侧空白处)
  1164. function closeFloatingComments() {
  1165. const closeFloatingCommentsModal = (mutationsList, observer) => {
  1166. for (const mutation of mutationsList) {
  1167. for (const target of mutation.addedNodes) {
  1168. if (target.nodeType != 1) return
  1169. if (target.querySelector('.Modal-backdrop')) {
  1170. document.querySelector('.Modal-backdrop').onclick = function(event){
  1171. if (event.target == this) {
  1172. let button = document.querySelector('.Button.Modal-closeButton.Button--plain');
  1173. if (button) button.click();
  1174. }
  1175. }
  1176. }
  1177. }
  1178. }
  1179. };
  1180. const observer = new MutationObserver(closeFloatingCommentsModal);
  1181. observer.observe(document, { childList: true, subtree: true });
  1182. }
  1183.  
  1184.  
  1185. // 监听 XMLHttpRequest 事件
  1186. /*function EventXMLHttpRequest() {
  1187. var _send = window.XMLHttpRequest.prototype.send
  1188. function sendReplacement(data) {
  1189. addTypeTips();
  1190. return _send.apply(this, arguments);
  1191. }
  1192. window.XMLHttpRequest.prototype.send = sendReplacement;
  1193. }*/
  1194.  
  1195.  
  1196. // 自定义 locationchange 事件(用来监听 URL 变化)
  1197. function addLocationchange() {
  1198. history.pushState = ( f => function pushState(){
  1199. var ret = f.apply(this, arguments);
  1200. window.dispatchEvent(new Event('pushstate'));
  1201. window.dispatchEvent(new Event('locationchange'));
  1202. return ret;
  1203. })(history.pushState);
  1204.  
  1205. history.replaceState = ( f => function replaceState(){
  1206. var ret = f.apply(this, arguments);
  1207. window.dispatchEvent(new Event('replacestate'));
  1208. window.dispatchEvent(new Event('locationchange'));
  1209. return ret;
  1210. })(history.replaceState);
  1211.  
  1212. window.addEventListener('popstate',()=>{
  1213. window.dispatchEvent(new Event('locationchange'))
  1214. });
  1215. }
  1216.  
  1217.  
  1218. // [完整显示时间 + 置顶显示时间] 功能修改自:https://greasyfork.org/scripts/402808(从 JQuery 改为原生 JavaScript,且精简、优化了代码)
  1219. // 完整显示时间 + 置顶显示时间 - 首页
  1220. function topTime_index() {
  1221. let topTime = document.querySelectorAll('.TopstoryItem');if (!topTime) return
  1222. topTime.forEach(function(_this) {
  1223. let t = _this.querySelector('.ContentItem-time');if (!t) return
  1224. if (!(t.classList.contains('full')) && t.querySelector('span') && t.querySelector('span').innerText != null) {
  1225. // 完整显示时间
  1226. topTime_allTime(t)
  1227. // 发布时间置顶
  1228. topTime_publishTop(t, _this, 'ContentItem-meta')
  1229. }
  1230. });
  1231. }
  1232.  
  1233.  
  1234. // 完整显示时间 + 置顶显示时间 - 回答页
  1235. function topTime_question() {
  1236. let topTime = document.querySelectorAll('.ContentItem.AnswerItem');if (!topTime) return
  1237. topTime.forEach(function(_this) {
  1238. let t = _this.querySelector('.ContentItem-time');if (!t) return
  1239. if (!(t.classList.contains('full')) && t.querySelector('span') && t.querySelector('span').innerText != null) {
  1240. // 完整显示时间
  1241. topTime_allTime(t)
  1242. // 发布时间置顶
  1243. topTime_publishTop(t, _this, 'ContentItem-meta')
  1244. }
  1245.  
  1246. });
  1247.  
  1248. // 问题创建时间
  1249. if (!(document.querySelector('.QuestionPage .QuestionHeader-side .QuestionTime-xiu')) && location.href.indexOf('/log') == -1) {
  1250. document.querySelector('.QuestionPage .QuestionHeader-side').insertAdjacentHTML('beforeEnd', '<div class="QuestionTime-xiu" style="color:#9098ac; margin-top:10px"><p>创建时间:' + getUTC8(new Date(document.querySelector('.QuestionPage > meta[itemprop=dateCreated]').content)) + '</p><p>最后编辑:' + getUTC8(new Date(document.querySelector('.QuestionPage > meta[itemprop=dateModified]').content)) + '</p></div>');
  1251. }
  1252. }
  1253.  
  1254.  
  1255. // 完整显示时间 + 置顶显示时间 - 搜索结果页
  1256. function topTime_search() {
  1257. let topTime = document.querySelectorAll('.ContentItem.AnswerItem, .ContentItem.ArticleItem');if (!topTime) return
  1258. topTime.forEach(function(_this) {
  1259. let t = _this.querySelector('.ContentItem-time');if (!t) return
  1260. if (!(t.classList.contains('full')) && t.querySelector('span') && t.querySelector('span').innerText != null) {
  1261. // 完整显示时间
  1262. topTime_allTime(t)
  1263. // 发布时间置顶
  1264. topTime_publishTop(t, _this, 'SearchItem-meta')
  1265. }
  1266.  
  1267. });
  1268. }
  1269.  
  1270.  
  1271. // 完整显示时间 + 置顶显示时间 - 用户主页
  1272. function topTime_people() {
  1273. let topTime = document.querySelectorAll('.ContentItem.AnswerItem, .ContentItem.ArticleItem');if (!topTime) return
  1274. topTime.forEach(function(_this) {
  1275. let t = _this.querySelector('.ContentItem-time');if (!t) return
  1276. if (!(t.classList.contains('full')) && t.querySelector('span') && t.querySelector('span').innerText != null) {
  1277. // 完整显示时间
  1278. topTime_allTime(t)
  1279. // 发布时间置顶
  1280. topTime_publishTop(t, _this, 'ContentItem-meta')
  1281. }
  1282.  
  1283. });
  1284. }
  1285.  
  1286.  
  1287. // 完整显示时间 + 置顶显示时间 - 专栏/文章
  1288. function topTime_zhuanlan() {
  1289. let t = document.querySelector('.ContentItem-time');if (!t) return
  1290. // 完整显示时间
  1291. if (t.innerText.indexOf('编辑于') > -1 && !(t.classList.contains('xiu-time'))) {
  1292. let bianjiyu = t.innerText;
  1293. t.click();
  1294. t.innerText = (t.innerText + "," + bianjiyu)
  1295. t.classList.add('xiu-time');
  1296. }
  1297.  
  1298. //发布时间置顶
  1299. if (menu_value('menu_publishTop') && !(document.querySelector('.Post-Header > .ContentItem-time')) && !(document.querySelector('.ContentItem-meta > .ContentItem-time'))) {
  1300. t.style.cssText = 'padding:0px 0px 0px 0px; margin-top: 14px'
  1301. let temp_time = t.cloneNode(true);
  1302. // t.style.display = 'none';
  1303. if (location.href.indexOf('/column/') > -1){
  1304. document.querySelector('.ContentItem-meta').insertAdjacentElement('beforeEnd', temp_time);
  1305. } else {
  1306. document.querySelector('.Post-Header').insertAdjacentElement('beforeEnd', temp_time);
  1307. }
  1308. }
  1309. }
  1310.  
  1311.  
  1312. // 完整显示时间
  1313. function topTime_allTime(t) {
  1314. if (t.textContent.indexOf('发布于') > -1 && t.textContent.indexOf('编辑于') == -1) {
  1315. t.querySelector('span').textContent = (t.querySelector('span').dataset.tooltip);
  1316. t.classList.add('full');
  1317. } else if (t.textContent.indexOf('发布于') == -1 && t.textContent.indexOf('编辑于') > -1) {
  1318. t.querySelector('span').textContent = (t.querySelector('span').dataset.tooltip) + ' ,' + (t.querySelector('span').textContent);
  1319. t.classList.add('full');
  1320. }
  1321. }
  1322.  
  1323.  
  1324. // 发布时间置顶
  1325. function topTime_publishTop(t, _this, _class) {
  1326. if (!menu_value('menu_publishTop')) return
  1327. if (!t.parentNode.classList.contains(_class)) {
  1328. let temp_time = t.cloneNode(true);
  1329. _this.querySelector('.' + _class).insertAdjacentElement('beforeEnd', temp_time);
  1330. }
  1331. }
  1332.  
  1333.  
  1334. // UTC 标准时转 UTC+8 北京时间,修改自:https://greasyfork.org/zh-CN/scripts/402808(精简)
  1335. function getUTC8(t) {
  1336. return (t.getFullYear() + '-' + (((t.getMonth() + 1) < 10) ? ('0' + (t.getMonth() + 1)) : (t.getMonth() + 1)) + '-' + ((t.getDate() < 10) ? ('0' + t.getDate()) : t.getDate()) + '\xa0\xa0' + ((t.getHours() < 10) ? ('0' + t.getHours()) : t.getHours()) + ':' + ((t.getMinutes() < 10) ? ('0' + t.getMinutes()) : t.getMinutes()) + ':' + ((t.getSeconds() < 10) ? ('0' + t.getSeconds()) : t.getSeconds()));
  1337. }
  1338.  
  1339.  
  1340. // 默认站外直链,修改自:https://greasyfork.org/scripts/402808(从 JQuery 改为原生 JavaScript,且精简、优化了代码)
  1341. function directLink () {
  1342. document.querySelectorAll('a.external[href*="link.zhihu.com/?target="], a.LinkCard[href*="link.zhihu.com/?target="]:not(.MCNLinkCard):not(.ZVideoLinkCard):not(.ADLinkCardContainer)').forEach(function (_this) {_this.href = decodeURIComponent(_this.href.substring(_this.href.indexOf('link.zhihu.com/?target=') + 23));});
  1343. }
  1344.  
  1345.  
  1346. // 默认高清原图,修改自:https://greasyfork.org/scripts/402808(从 JQuery 改为原生 JavaScript,且精简、优化了代码)
  1347. function originalPic(){
  1348. document.querySelectorAll('img').forEach(function(one){if (one.dataset.original != undefined && one.src != one.dataset.original && one.className != 'comment_sticker') {one.src = one.dataset.original}});
  1349. }
  1350.  
  1351.  
  1352. // 默认折叠邀请,修改自:https://greasyfork.org/scripts/402808(从 JQuery 改为原生 JavaScript,且精简、优化了代码)
  1353. function questionInvitation(){
  1354. let time = setInterval(function(){
  1355. let q = document.querySelector('.QuestionInvitation-content'); if (!q) return
  1356. clearInterval(time);
  1357. q.style.display = 'none';
  1358. document.querySelector('.QuestionInvitation-title').innerHTML = document.querySelector('.QuestionInvitation-title').innerText + '<span style="cursor: pointer; font-size: 14px; color: #919aae;"> 展开/折叠</span>'
  1359. // 点击事件(展开/折叠)
  1360. document.querySelector('.Topbar').onclick = function(){
  1361. let q = document.querySelector('.QuestionInvitation-content')
  1362. if (q.style.display == 'none') {
  1363. q.style.display = ''
  1364. } else {
  1365. q.style.display = 'none'
  1366. }
  1367. }
  1368. });
  1369. }
  1370.  
  1371.  
  1372. (function() {
  1373. addLocationchange();
  1374. removeLogin(); // 移除登录弹窗
  1375. setInterval(originalPic,100); // 默认高清原图
  1376. setInterval(directLink, 100); // 默认站外直链
  1377. window.addEventListener('locationchange', function(){ // 针对的是从单个回答页跳转到完整回答页时
  1378. if (location.pathname.indexOf('question') > -1 && location.pathname.indexOf('waiting') === -1 && location.pathname.indexOf('/answer/') === -1) { // 回答页 //
  1379. setTimeout(function(){
  1380. collapsedNowAnswer('.QuestionPage'); // 收起当前回答 + 快捷返回顶部
  1381. collapsedNowAnswer('.Question-main'); // 收起当前回答 + 快捷返回顶部
  1382. questionRichTextMore(); // 展开问题描述
  1383. blockUsers('question'); // 屏蔽指定用户
  1384. blockYanXuan(); // 屏蔽盐选内容
  1385. }, 300);
  1386. }
  1387. })
  1388.  
  1389. if (GM_info.scriptHandler === 'Violentmonkey') { // Violentmonkey 比 Tampermonkey 加载更早,会导致一些元素还没加载,因此需要延迟一会儿
  1390. setTimeout(start, 300);
  1391. } else {
  1392. start();
  1393. }
  1394.  
  1395. function start(){
  1396. removeHighlightLink(); // 移除高亮链接
  1397. if (location.hostname != 'zhuanlan.zhihu.com') {
  1398. collapsedAnswer(); // 一键收起回答
  1399. questionInvitation(); // 默认折叠邀请
  1400. }
  1401. closeFloatingComments(); // 快捷关闭悬浮评论(监听点击事件,点击网页两侧空白处)
  1402. blockKeywords('comment'); // 屏蔽指定关键词(评论)
  1403. if (location.pathname.indexOf('question') > -1) { // 回答页 //
  1404. if (location.pathname.indexOf('waiting') == -1) {
  1405. collapsedNowAnswer('.QuestionPage'); // 收起当前回答 + 快捷返回顶部
  1406. collapsedNowAnswer('.Question-main'); // 收起当前回答 + 快捷返回顶部
  1407. questionRichTextMore(); // 展开问题描述
  1408. blockUsers('question'); // 屏蔽指定用户
  1409. blockYanXuan(); // 屏蔽盐选内容
  1410. blockType('question'); // 屏蔽指定类别(视频/文章等)
  1411. defaultCollapsedAnswer(); // 默认收起回答
  1412. }
  1413. setInterval(topTime_question, 300); // 置顶显示时间
  1414. } else if (location.pathname === '/search') { // 搜索结果页 //
  1415. collapsedNowAnswer('main div'); // 收起当前回答 + 快捷返回顶部
  1416. collapsedNowAnswer('.Search-container'); // 收起当前回答 + 快捷返回顶部
  1417. setInterval(topTime_search, 300); // 置顶显示时间
  1418. addTypeTips(); // 区分问题文章
  1419. addToQuestion(); // 直达问题按钮
  1420. blockUsers('search'); // 屏蔽指定用户
  1421. blockKeywords('search'); // 屏蔽指定关键词
  1422. blockType('search'); // 屏蔽指定类别(视频/文章等)
  1423. } else if (location.pathname.indexOf('/topic/') > -1) { // 话题页 //
  1424. if (location.pathname.indexOf('/hot') > -1 || location.href.indexOf('/top-answers') > -1) { // 仅限 [讨论] [精华]
  1425. collapsedNowAnswer('main.App-main'); // 收起当前回答 + 快捷返回顶部
  1426. collapsedNowAnswer('.ContentLayout'); // 收起当前回答 + 快捷返回顶部
  1427. setInterval(topTime_people, 300); // 置顶显示时间
  1428. addTypeTips(); // 区分问题文章
  1429. addToQuestion(); // 直达问题按钮
  1430. blockUsers('topic'); // 屏蔽指定用户
  1431. blockKeywords('topic'); // 屏蔽指定关键词
  1432. }
  1433. } else if (location.hostname === 'zhuanlan.zhihu.com'){ // 文章 //
  1434. backToTop('article.Post-Main.Post-NormalMain'); // 快捷返回顶部
  1435. backToTop('div.Post-Sub.Post-NormalSub'); // 快捷返回顶部
  1436. setInterval(topTime_zhuanlan, 300); // 置顶显示时间
  1437. blockUsers(); // 屏蔽指定用户
  1438. } else if (location.pathname.indexOf('/column/') > -1) { // 专栏 //
  1439. setTimeout(function(){
  1440. collapsedAnswer(); // 一键收起回答
  1441. collapsedNowAnswer('main div'); // 收起当前回答 + 快捷返回顶部
  1442. setInterval(topTime_zhuanlan, 300); // 置顶显示时间
  1443. blockUsers(); // 屏蔽指定用户
  1444. }, 300);
  1445. } else if (location.pathname.indexOf('/people/') > -1 || location.href.indexOf('org') > -1) { // 用户主页 //
  1446. if (location.pathname.split('/').length === 3) addTypeTips();addToQuestion(); // 区分问题文章、直达问题按钮
  1447. collapsedNowAnswer('main div'); // 收起当前回答 + 快捷返回顶部
  1448. collapsedNowAnswer('.Profile-main'); // 收起当前回答 + 快捷返回顶部
  1449. setInterval(topTime_people, 300); // 置顶显示时间
  1450. blockUsers('people'); // 屏蔽指定用户
  1451. blockKeywords('people'); // 屏蔽指定关键词
  1452. } else if (location.pathname.indexOf('/collection/') > -1) { // 收藏夹 //
  1453. addTypeTips(); // 区分问题文章
  1454. addToQuestion(); // 直达问题按钮
  1455. collapsedNowAnswer('main'); // 收起当前回答 + 快捷返回顶部
  1456. collapsedNowAnswer('.CollectionsDetailPage'); // 收起当前回答 + 快捷返回顶部
  1457. setInterval(topTime_people, 300); // 置顶显示时间
  1458. blockKeywords('collection'); // 屏蔽指定关键词
  1459. } else { // 首页 //
  1460. collapsedNowAnswer('main div'); // 收起当前回答 + 快捷返回顶部
  1461. collapsedNowAnswer('.Topstory-container'); // 收起当前回答 + 快捷返回顶部
  1462. setInterval(topTime_index, 300); // 置顶显示时间
  1463. addTypeTips(); // 区分问题文章
  1464. addToQuestion(); // 直达问题按钮
  1465. blockUsers('index'); // 屏蔽指定用户
  1466. blockKeywords('index'); // 屏蔽指定关键词
  1467. blockType(); // 屏蔽指定类别(视频/文章等)
  1468. // 解决屏蔽视频后,因为首页信息流太少而没有滚动条导致无法加载更多内容的问题
  1469. if (menu_value('menu_blockTypeVideo')) document.lastElementChild.appendChild(document.createElement('style')).textContent = '.Topstory-container{min-height: 1500px;}';
  1470. }
  1471. }
  1472. })();