本文轉自:https://ithelp.ithome.com.tw/articles/10338948

前情提要

資訊大爆炸。

有時候我們瀏覽技術文章,不一定真的是想學深奧的高級技術。
然而劣質低端的文章充斥著,則會降低我們學習的效率、甚至變成噪音與雜訊,干擾我們的思緒。
因此針對某些洗文或是質量很低的作者,我們必須列為思想犯,
否則會降低閱讀質量,平白浪費自己的時間、平台的版面、上網的電力、看到垃圾資訊的副作用等等....

構思來源

如果有個思想審查警衛可以:去除那些垃圾低端,稱不上技術文章的雜訊。
以確保未來瀏覽文章的時候,不會再被洗文打擾,
也可以針對不喜歡的主題去封鎖,讓時間與精神更能專注於自己想要學的資訊。

阻止一些垃圾就是喜歡把自己尚未整理的白痴內容一直丟上來,
什麼都還不懂,把技術文章當成個人日記簿,寫一堆自我囈語、無病呻吟,
每天大量狂發文章,昭告天下以為這就是努力,欺騙自己也浪費別人的人生。

「思想審查警衛」出動!

功能

  1. 把頭像屏蔽
  2. 加上思想通緝犯、紅字與刪除線
  3. 新增封殺按鈕
  4. 版面通知封殺名單與文章數
  5. 透過ajax確認某id的最新ID

效果截圖


此截圖僅是腳本示範,跟其使用者無關,
本人沒有任何覺得此使用者發的文章是差勁的意味,我認為非常上進、值得學習。


此圖也只是隨機挑user使用,純粹作為範例用途,不代表我個人意見。

腳本下載

https://greasyfork.org/zh-TW/scripts/477283-%E6%80%9D%E6%83%B3%E7%8A%AF%E5%B0%81%E6%AE%BA

原理說明

鄭重聲明,這個示範真的毫無任何私人意味,此腳本也只是針對不同的主題去隱藏,
例如我想學python就不想看到JS的文章,因此使用此工具幫忙隱藏罷了。
取名做思想警察、比喻成清除垃圾等都只是文學上的趣味。
請勿當真Ꮚ・ꈊ・Ꮚ

這次的技術可說比較難,認真難很多!但是趣味性以及功能性是無比的超越!
可以說幾乎是我寫系列文以來,最頂、最派的一篇!
不過很多觀念已經出現過,在【前端小試身手】系列裡面,每次的腳本都是主打實用,
因此cookie或localstorage這種技術當然都會運用到。

JS原始碼

// ==UserScript==
// @license MIT
// @name         👮思想犯封殺
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  把廢文製造機轟出去
// @author       You
// @match       https://ithelp.ithome.com.tw/articles*
// @match        https://ithelp.ithome.com.tw/users/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ithome.com.tw
// @grant        none
// @run-at document-end
// ==/UserScript==

let URL = window.location.href;
let ArticleSite = "https://ithelp.ithome.com.tw/articles?tab=tech"
let UserSite = "https://ithelp.ithome.com.tw/users/"

// 判斷URL的開頭部分
if (URL.startsWith(ArticleSite)) {
    //文章頁執行清理垃圾程序
    CleanGarbage();
} else if (URL.startsWith(UserSite)) {
    UserCheck();
} else {
    console.log("這邊不執行腳本");
}

// 餅乾儲存的機制函數-------------------------------------------------
function setListInCookie(list) {
    document.cookie = 'myList=' + JSON.stringify(list) + '; expires=Wed, 31 Dec 2099 23:59:59 GMT;';
}

// 從 Cookie 中獲取 list
function getListFromCookie() {
    var cookieValue = document.cookie.replace(/(?:(?:^|.*;\s*)myList\s*\=\s*([^;]*).*$)|^.*$/, "$1");
    return JSON.parse(cookieValue) || [];
}

function CleanGarbage(){

// 從本地存儲中獲取數據
var myListData = localStorage.getItem("myList");
// 解析數據到變量 list
var list = myListData ? JSON.parse(myListData) : [];

//list = getListFromCookie()||[]; // 儲存要刪除的字符串名單
// 找到所有CLASS是"qa-list__info-link"的<a>元素
var linkElements = document.querySelectorAll('.qa-list__info-link');
var removedCount = 0; // 初始化已清除的垃圾數量

for (var j = 0; j < list.length; j++) {

// 遍歷這些<a>元素,確保文本內容包含"伍貳捌",然後刪除其父元素
for (var i = 0; i < linkElements.length; i++) {
    if (linkElements[i].textContent.includes(list[j])) {
        // 開始向上查找父元素
        var parentElement = linkElements[i].parentElement;

        while (parentElement) {
            // 如果找到具有"classname"為"qa-list"的<div>元素,則刪除它
            if (parentElement.classList.contains('qa-list')) {
                parentElement.remove();
                removedCount++; // 增加清除的數量
                console.warn('抓到"'+list[j]+'"這位思想犯');
                break; // 找到並刪除後,結束循環
            }

            parentElement = parentElement.parentElement;
        }
    }
}

    if (removedCount>0){
        // 顯示已清除的垃圾數量
        console.log('已清除他的 ' + removedCount + ' 篇垃圾');}
removedCount=0;
}
}
//-------------------------------------------------------
// 為了防止五百八改名,我們針對他的ID去ajax得到他最新的名稱
function FindBitch() {
  // 使用 Fetch API 獲取指定 URL 的內容
  return fetch("https://ithelp.ithome.com.tw/users/20163468")
    .then(response => response.text())
    .then(data => {
      // 創建一個臨時 div 元素以容納頁面內容
      var tempDiv = document.createElement("div");
      tempDiv.innerHTML = data;

      // 查找 class 為 "profile-header__name" 的元素
      var profileNameElement = tempDiv.querySelector(".profile-header__name");

      if (profileNameElement) {
        // 刪除元素內的所有 <span> 元素
        var spanElements = profileNameElement.querySelectorAll("span");
        spanElements.forEach(function(span) {
          span.remove();
        });

        // 讀取元素的文本內容,去掉前導和尾隨空格
        var text = profileNameElement.textContent.trim();

        // 返回處理後的文本內容
        return text;
      } else {
        return "未找到元素";
      }
    })
    .catch(error => {
      console.error("發生錯誤: " + error);
      return "發生錯誤";
    });
}

//-------------------------------------------------------

function UserCheck(){
  //轉換資料從餅乾到localstorage
                        var currentCookieValue = getCookie("myList");

                        // 2. 存儲數據到本地存儲
                        if (currentCookieValue) {
                            var list = JSON.parse(currentCookieValue);
                            // 存儲到本地存儲
                            localStorage.setItem("myList", JSON.stringify(list));
                        }else{

                            FindBitch()
                                .then(text => {
                                let FirstKill = [text];
                                setListInCookie(FirstKill);
                            })
                                .catch(error => {
                                console.error("找不到五百八:", error);
                            });

                        }

// 刪除不需要的ID
document.querySelector('.profile-header__account').remove();
//封殺按鈕-------------------------------------------------
// 找到具有class為"profile-header__right"的元素
var profileRightElement = document.querySelector('.profile-header__right');
var pullRightElement = profileRightElement.querySelector('.pull-right');

        // 創建一個新按鈕元素
        var BlockBtn = document.createElement('button');
        BlockBtn.textContent = '封殺';
        // 添加樣式和類名到按鈕
        BlockBtn.style.marginTop = '10px';
        BlockBtn.style.width = '100%';
        BlockBtn.className = 'btn btn-trace trace_btn_border BlockBtn';

        // 將按鈕元素添加到"pull-right"元素內部
        pullRightElement.appendChild(BlockBtn);

// 通緝犯名單的cookie------------------------------------------------

// 從 Cookie 中加載 list(例如,頁面加載時)
list = getListFromCookie()||[];
let UserBlock = document.querySelector('.profile-header__name');
let text = UserBlock.textContent.trim();

 // 如果使用者已經在封殺名單內的判斷,已存在或不存在
                if (list.includes(text)) {
                        BlockStart(BlockBtn);
                    }
                    else
                    {
                    // 針對封殺按鈕進行監聽事件
                    BlockBtn.addEventListener('click', function() {
                    list.push(text);
                    setListInCookie(list);

                    //先加入到名單內,然後再執行封殺事件
                    BlockStart();
                    // 輸出到控制台
                    console.log('黑名單新增:' + text);

                        //本地儲存機制-----------------------------
                        let currentCookieValue = getCookie("myList");
                        let list2 = JSON.parse(currentCookieValue);
                        // 存儲到本地存儲
                        localStorage.setItem("myList", JSON.stringify(list2));

        });
                    }
}

// ------------------------------------------------

//封殺事件的函數
function BlockStart(){
    let BlockBtn = document.querySelector('.BlockBtn');
                   BlockBtn.textContent = '已封殺';
                    BlockBtn.disabled = true;
                    BadText();
                    BadImg();
}

//封殺事件函數裡面的細項函數

function BadText(){
// 標記這傢夥是垃圾-------------------------------------------------
    let UserBlock = document.querySelector('.profile-header__name');
    let newHeading = document.createElement('h1');
    newHeading.textContent = '思想通緝犯';

    // 把思想通緝犯這幾個大字加上去
    UserBlock.parentElement.insertBefore(newHeading, UserBlock);
    UserBlock.style.textDecoration = "line-through";
    UserBlock.style.color = "red";

}

function BadImg(){
//圖片進行網點作業XD-------------------------------------------------
var originalImage = document.querySelector('.profile-header__avatar');

// 創建一個包含交叉紅線的覆蓋層 <div> 元素
var overlayDiv = document.createElement('div');
overlayDiv.style.position = 'absolute';
overlayDiv.style.width = '150px';
overlayDiv.style.height = '150px';
overlayDiv.style.background = 'linear-gradient(45deg, black 50%, transparent 50%), linear-gradient(-45deg, black 50%, transparent 50%)';
overlayDiv.style.backgroundSize = '5px 5px, 5px 5px';

overlayDiv.style.backgroundPosition = '0 0, 0 2px';

// 將覆蓋層疊加到圖片上
originalImage.parentNode.appendChild(overlayDiv);

// 設置覆蓋層的位置,以與原始圖像對齊
overlayDiv.style.top = originalImage.offsetTop + 'px';
overlayDiv.style.left = originalImage.offsetLeft + 'px';

// 設置覆蓋層的z-index,以確保它在圖片上方
overlayDiv.style.zIndex = '2';
}

觀念筆記

這個腳本開發足足花了我一整個晚上,將近八小時之久。
有趣的是,其中為了進行測試才選某些user當作隱藏對象,否則腳本執行上會出錯。
細心的人若觀察原始碼,也會發現裡面有個firstKill,
那是必須要的段落,先設置好cookie的首要內容物,才有辦法繼續操作下去( ิ◕㉨◕ ิ)

也就是初始化的概念XD

另一個有趣的點是,為了防止使用者改名導致腳本出錯,我甚至不惜再寫一段ajax,
去更新一下ID,這樣不管人家怎麼改,都逃不了,
要改成「別抓我」也沒用,這個腳本都還是可以run。

心得後記

我只能說這篇是自從「備份IT幫發文、一眼全覽」最強的JS教學文章!
超派,真的不騙( メ∀・)
有些人喜歡看前端小試身手,有些人喜歡前端動手玩創意;
其實這兩個系列的本質都是JS的教學,是可以互相連接的,但也有很多不同的重心。

這個系列就是以腳本為主,重點在於創意與發想,打到使用者痛點。
【前端動手玩創意】則是以建構網站為起點,
任何元素與概念都會變成網頁上的一部分,算是比較基礎工的建立。

如果對JS的強大感興趣,那麼可以把這兩個系列交互看,反覆的閱讀、實際動手操作,
這樣一來的學習非常踏實,甚至比YT學習都來的高效率、實際。
尤其此系列都是原創發想的腳本,當然超強!
喜歡記得關注,未來還有更多超酷的前端內容可以玩,下課⧸⎩⎠⎞͏(・∀・)⎛͏⎝⎭⧹

按讚的人:

共有 0 則留言