├── .gitignore ├── README.md ├── ZhihuFilter.crx ├── demo-images ├── block1.png └── popup-demo.png ├── images ├── cross-15px.png ├── cross-hover-15px.png └── icon.png ├── js ├── background.js ├── content_script.js ├── jquery-2.2.1.js ├── options.js └── popup.js ├── manifest.json ├── options.html ├── popup.html └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 这是一个用于知乎网站的Chrome扩展,用于屏蔽网站首页的时间线上不感兴趣的内容。 2 | 3 | 因为知乎的设置,一个问题只能添加5个话题,而且屏蔽的话题也有上限,所以有时屏蔽了相关话题不能够保证问题不会出现在时间线上。 4 | 5 | 这个扩展会检测首页的每个答案,如果题目或答案中出现了你想要屏蔽的关键词,这个答案会默认隐藏,当然你也可以点击按钮重新查看答案。 6 | 7 | 在如下页面修改需要屏蔽的词语: 8 | 9 | ![](demo-images/popup-demo.png) 10 | 11 | 屏蔽后的答案如下所示: 12 | 13 | ![](demo-images/block1.png) 14 | 15 | 相关博客: 16 | [开发一个用于屏蔽知乎网内容的Chrome扩展](http://www.wukai.me/2016/03/25/chrome-extension-zhihufilter/) 17 | 18 | ### 尚未开发功能及需要改进的地方: 19 | 20 | - 除了首页外,问题页面是否需要屏蔽关键词 21 | 22 | - 加入和删除关键词时增加一个淡出的提示 23 | 24 | - 可以选择是否显示问题部分 25 | 26 | - 在右键菜单中加入增加关键词的选项 27 | 28 | - 正则表达式支持 29 | 30 | - icon是显示在地址栏的外部,而不是内部的最右侧(似乎是新版的Chrome特性) 31 | -------------------------------------------------------------------------------- /ZhihuFilter.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noiron/ZhihuFilter/b3928a1e7da1cc61568b7ef1b9ec1468b14d791d/ZhihuFilter.crx -------------------------------------------------------------------------------- /demo-images/block1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noiron/ZhihuFilter/b3928a1e7da1cc61568b7ef1b9ec1468b14d791d/demo-images/block1.png -------------------------------------------------------------------------------- /demo-images/popup-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noiron/ZhihuFilter/b3928a1e7da1cc61568b7ef1b9ec1468b14d791d/demo-images/popup-demo.png -------------------------------------------------------------------------------- /images/cross-15px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noiron/ZhihuFilter/b3928a1e7da1cc61568b7ef1b9ec1468b14d791d/images/cross-15px.png -------------------------------------------------------------------------------- /images/cross-hover-15px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noiron/ZhihuFilter/b3928a1e7da1cc61568b7ef1b9ec1468b14d791d/images/cross-hover-15px.png -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noiron/ZhihuFilter/b3928a1e7da1cc61568b7ef1b9ec1468b14d791d/images/icon.png -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | // Called when the user clicks on the browser action. 2 | //chrome.browserAction.onClicked.addListener(function(tab) { 3 | // // No tabs or host permissions needed! 4 | // 5 | // chrome.tabs.executeScript(null, {file: "jquery-2.2.1.js"}); 6 | // chrome.tabs.executeScript(null, {file: "content_script.js"}); 7 | //}); 8 | 9 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 10 | if (request.method == "getKeywords") 11 | if (localStorage['keywords']) { 12 | sendResponse({keywords: localStorage['keywords']}); 13 | } else { 14 | localStorage.setItem('keywords', ''); 15 | } 16 | else 17 | sendResponse({}); 18 | }); 19 | 20 | // show the popup when the user clicks on the page action. 21 | //chrome.pageAction.onClicked.addListener(function(tab) { 22 | // chrome.pageAction.show(tab.id); 23 | // chrome.tabs.executeScript(null, {file: "jquery-2.2.1.js"}); 24 | // chrome.tabs.executeScript(null, {file: "content_script.js"}); 25 | //}); 26 | 27 | // listen for any changes to the URL of any tab. 28 | chrome.tabs.onUpdated.addListener(function(id, info, tab){ 29 | if (tab.url.toLowerCase().indexOf("zhihu.com") > -1){ 30 | chrome.pageAction.show(id); 31 | chrome.pageAction.setPopup({ 32 | tabId: id, 33 | popup: 'popup.html' 34 | }); 35 | } 36 | }); 37 | 38 | -------------------------------------------------------------------------------- /js/content_script.js: -------------------------------------------------------------------------------- 1 | console.log("扩展开始运行……"); 2 | 3 | // 用于测试的关键字 4 | var testKeywords = ["疯狂动物城", "攻壳机动队", "AlphaGo", "papi酱", "Negar", "比亚迪", "和菜头", "小米5"]; 5 | var str = ''; 6 | var keywords = []; 7 | var count = 0; // 记录一下对页面处理了多少次,测试用 8 | 9 | // 从扩展的localStorage中获得存储的关键词 10 | chrome.runtime.sendMessage({method: "getKeywords"}, function (response) { 11 | str = response.keywords; 12 | keywords = str !== '' ? str.split(',') : []; 13 | }); 14 | 15 | // 创建用于替换的div,并设置其样式 16 | var $div = $('
'); 17 | $div.append($('

这里有一个被屏蔽的答案

')) 18 | .append($('')) 19 | .css({ 20 | "backgroundColor" : "#EFF6FA", 21 | "height":"64px" 22 | }); 23 | 24 | // keywords是异步获取的,可能运行至此,值还没有传过来,所以用延时来解决 25 | if (keywords.length === 0) { 26 | setTimeout(function() { 27 | processPage(); 28 | setStyle(); 29 | }, 1500); 30 | } else { 31 | processPage(); 32 | setStyle(); 33 | } 34 | 35 | // 当页面加载更多答案的时候,重新运行处理程序 36 | // 使用MutationObserver来检测页面的变动 37 | var MutationObserver = window.MutationObserver 38 | || window.WebKitMutationObserver; 39 | var observer = new MutationObserver(function(mutationRecords) { 40 | for (var i = 0; i < mutationRecords.length; i++) { 41 | var mutation = mutationRecords[i]; 42 | if (mutation.addedNodes.length > 0) { 43 | var $addNode = $(mutation.addedNodes); 44 | // 检查新增的节点里是否有 class=feed-main 的元素 45 | if($addNode.find("div.feed-main").length > 0) { 46 | // TODO: 这里获得了新增的每一条答案,需要把函数改成对每一条答案单独处理,而不是在一个函数内处理所有的内容 47 | processPage(); 48 | setStyle(); 49 | return; 50 | } 51 | } 52 | } 53 | }); 54 | var observerOption = { 55 | 'childList': true, 56 | 'subtree': true, 57 | 'attributes': false 58 | }; 59 | observer.observe($('#js-home-feed-list')[0], observerOption); 60 | 61 | var extensionOption = getOptions(); 62 | 63 | /* 主要的处理函数 */ 64 | function processPage() { 65 | var allContents = $(".feed-main"); 66 | for (var i = 0; i < allContents.length; i++) { 67 | var outerHTML = allContents[i].outerHTML; 68 | for (var j = 0; j < keywords.length; j++) { 69 | var keyword = keywords[j]; 70 | if (!extensionOption.caseSensitive) { // 不区分大小写 71 | keyword = keyword.toLowerCase(); 72 | outerHTML = outerHTML.toLowerCase(); 73 | } 74 | if (keyword !== '') { // 防止出现所有答案都被屏蔽的情况(可以用其它的方法来避免) 75 | if (outerHTML.indexOf(keyword) >= 0 && 76 | $(allContents[i]).siblings('.block-info').length === 0) { 77 | $(allContents[i]).addClass('hidden'); 78 | // 复制并插入前面创建的div 79 | var $divClone = $div.clone(); 80 | $divClone.find('span').text(' 【屏蔽的关键词为:' + keyword + '】'); 81 | $divClone.insertAfter(allContents[i]); 82 | 83 | // 给当前加入的div中的按钮上加入点击事件 84 | addBtnEvent($(allContents[i]).siblings('.block-info').children('.block-btn')); 85 | break; 86 | } 87 | } 88 | } 89 | } 90 | count += 1; 91 | console.log("ZhihuFilter扩展已运行次数:" + count); 92 | } 93 | 94 | function setStyle() { 95 | $('.block-btn').css({ 96 | "position" : "absolute", 97 | "left" : "50%", 98 | "bottom" : "12px", 99 | "border" : "none", 100 | "border-radius" : "3px", 101 | "padding" : "1px 3px", 102 | "margin-bottom" : "3px", 103 | "font-size" : "0.9em", 104 | "color" : "#fff", 105 | "opacity" : ".8", 106 | "backgroundColor" : "#81baeb", 107 | "cursor" : "pointer" 108 | }); 109 | $('.block-info>p').css({ 110 | "font-size" : "0.9em", 111 | "padding" : "10px 0 10px 0", 112 | "text-align" : "center" 113 | }); 114 | } 115 | 116 | function addBtnEvent(btn) { 117 | btn.on('click', function() { 118 | $(this).parent().siblings('.feed-main').toggleClass('hidden'); 119 | $(this).parent().find('p').toggleClass('hidden'); 120 | 121 | // 改变按钮上的文字及样式 122 | if ($(this).text().indexOf("手贱") >= 0) { 123 | $(this).parent().css({"height":"32px"}); 124 | if ($(this).text().indexOf("再手贱一下") >= 0) { 125 | $(this).text("我怎么就管不住这手呢?"); 126 | } else { 127 | $(this).text("啊,没防备啊!"); 128 | } 129 | } else { 130 | $(this).text("再手贱一下"); 131 | $(this).parent().css({"height":"64px"}); 132 | } 133 | }); 134 | 135 | // 鼠标移动到按钮上时改动其样式 136 | btn.mouseenter(function() { 137 | $(this).css({"opacity" : "1"}); 138 | }).mouseleave(function() { 139 | $(this).css({"opacity" : "0.8"}); 140 | }); 141 | } 142 | 143 | // 获取扩展的设置选项 144 | function getOptions() { 145 | var options = { 146 | 'caseSensitive': false 147 | }; 148 | chrome.storage.sync.get({ 149 | caseSensitive: false 150 | }, function (items) { 151 | options.caseSensitive = items.caseSensitive; 152 | }); 153 | return options; 154 | } 155 | -------------------------------------------------------------------------------- /js/options.js: -------------------------------------------------------------------------------- 1 | function saveOptions() { 2 | var caseSensitive = document.getElementById("case-sensitive").checked; 3 | chrome.storage.sync.set({ 4 | caseSensitive: caseSensitive 5 | }, function() { 6 | var status = document.getElementById("status"); 7 | status.textContent = "选项已保存"; 8 | setTimeout(function() { 9 | status.textContent = ""; 10 | }, 750); 11 | }); 12 | } 13 | 14 | function restoreOptions() { 15 | chrome.storage.sync.get({ 16 | caseSensitive: false 17 | }, function(items) { 18 | document.getElementById("case-sensitive").checked = items.caseSensitive; 19 | }); 20 | } 21 | 22 | document.addEventListener("DOMContentLoaded", restoreOptions); 23 | document.getElementById("save").addEventListener("click", saveOptions); -------------------------------------------------------------------------------- /js/popup.js: -------------------------------------------------------------------------------- 1 | var keywordsDisplay = localStorage.keywords.split(','); 2 | displayKeywords(); 3 | 4 | // 将所有想屏蔽的关键词放在单独的span里显示出来 5 | function displayKeywords() { 6 | // 清除已有的 span 元素,防止重复显示 7 | $('#display-keywords').empty(); 8 | keywordsDisplay = localStorage.keywords.split(','); 9 | for(var i = 0; i < keywordsDisplay.length; i++) { 10 | var wordSpan = document.createElement('span'); 11 | wordSpan.innerHTML = keywordsDisplay[i]; 12 | wordSpan.className = "item-word"; 13 | // 添加关键词右侧的删除按钮 14 | var removeButton = document.createElement('a'); 15 | removeButton.className = "remove-button"; 16 | removeButton.onclick = function() { 17 | removeFromLocalStorage(this.parentNode.firstChild.nodeValue); 18 | }; 19 | wordSpan.appendChild(removeButton); 20 | document.getElementById('display-keywords').appendChild(wordSpan); 21 | } 22 | } 23 | 24 | // 在输入框内按下回车会触发按钮单击事件 25 | $('#new-word').keydown(function(event) { 26 | if (event.keyCode === 13) { 27 | $('#save').trigger('click'); 28 | } 29 | }); 30 | 31 | $('#save').click(function(){ 32 | var $newWordInput = $('#new-word'); 33 | var newWord = $newWordInput.val(); 34 | 35 | if (newWord !== ' ') { // 这个判断用于防止把空格作为关键字 36 | if (localStorage.keywords && localStorage.keywords !== '') { 37 | if (keywordsDisplay.indexOf(newWord) > -1) { 38 | // 这里判断元素是否已存在 39 | alert(newWord + "已经存在"); 40 | return; 41 | } 42 | localStorage.keywords += (',' + newWord); 43 | } else { 44 | localStorage.keywords = newWord; 45 | } 46 | $newWordInput.val(""); 47 | displayKeywords(); 48 | } 49 | }); 50 | 51 | // 从localStorage中删除一个关键词 52 | function removeFromLocalStorage(value) { 53 | var values = localStorage.keywords.split(','); 54 | for(var i = 0 ; i < values.length ; i++) { 55 | if(values[i] === value) { 56 | values.splice(i, 1); 57 | localStorage.keywords = values.join(','); 58 | displayKeywords(); 59 | return; 60 | } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Zhihu Filter", 3 | "description": "屏蔽知乎首页不感兴趣的内容", 4 | "version": "0.1", 5 | "permissions": [ 6 | "tabs", 7 | "storage", 8 | "webNavigation", "*://*/*" 9 | ], 10 | "background": { 11 | "scripts": ["js/jquery-2.2.1.js","js/background.js"] 12 | }, 13 | "page_action": { 14 | "default_icon": "images/icon.png", 15 | "default_title": "知乎屏蔽扩展" 16 | }, 17 | "content_scripts": [ 18 | { 19 | "matches": ["*://*.zhihu.com/*"], 20 | "js": ["js/jquery-2.2.1.js", "js/content_script.js"] 21 | } 22 | ], 23 | "options_page": "options.html", 24 | "manifest_version": 2 25 | } -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

这里是知乎屏蔽扩展的选项设置页面

11 |

更多设置选项 is coming soon......

12 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 |
15 |

你已经屏蔽了下面的关键词:

16 |

17 |
18 |
19 |

请在下面输入想屏蔽的关键词(多个词语之间用英文逗号","隔开):

20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 400px; 3 | font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; 4 | font-size: 13px; 5 | line-height: 1.7; 6 | margin: 15px; 7 | } 8 | 9 | div { 10 | border-top: 1px solid #eee; 11 | } 12 | 13 | p#display-keywords { 14 | overflow: hidden; 15 | } 16 | 17 | div#input-keywords { 18 | margin-bottom: 25px; 19 | } 20 | 21 | .item-word { 22 | background: #eff6fa; 23 | color: #259; 24 | padding: 1px 10px 0; 25 | border-radius: 30px; 26 | margin: 0 5px 5px 0; 27 | float: left; 28 | } 29 | 30 | .remove-button { 31 | margin: 3px 0 -3px 5px; 32 | display: inline-block; 33 | width: 15px; 34 | height: 15px; 35 | background-color: blue; 36 | border-radius: 8px; 37 | cursor: pointer; 38 | background: url("images/cross-15px.png") no-repeat; 39 | } 40 | 41 | .remove-button:hover { 42 | background: url("images/cross-hover-15px.png") no-repeat; 43 | background-color: #225599; 44 | } --------------------------------------------------------------------------------