A few days ago the Nuggets that challenge the front end of the article is very hot, but dozens of praise hundreds of comments, indicating that this article is not the fire in quality but the fire in controversy. Objectively speaking, the problem inside is still good, can help us understand some of the mechanisms of JS. The authors comment mentioned several times in front section in the article quality problems, I want to say is more than the front section in other sections, too, any of the community may not ensure that all articles are fine, only the nuggets front-end section in the article number is more, this problem is more noticeable, but is not the same as the level of every reader, what do you think of this things of little use, But it can be very helpful for others.

So as the comments below a lot of people said that the hope of digging gold a shielding shielding function, can shield the author, shield the article. This function can indeed meet the needs of many people to screen articles, but this function is not a day or two, the official may have their own plans have not been put on the development agenda. I didn’t have such a requirement before. At most, I didn’t want to read articles about interview. However, after that article came out, I found that I still had a requirement to block it. Although I know that the official future will certainly come out of this function, but far from hydrolyzing the near thirst, it is better to develop a plug-in to achieve this function.

specifications

  1. Add the author you don’t like to the blacklist, and no longer display the blacklisted author’s articles on the home page and when searching for articles.
  2. Add keywords you don’t like to the title blacklist, and no longer display the keywords in the title on the home page and when searching for articles.
  3. The author can be blocked at any time on the article details page.
  4. You can manage (add and remove) blacklist lists.

The finished product to show

It is hereby noted that the author screen is only for the purpose of showing the function, and for the effect of deliberately selected more column of senior authors, there is no offense to the two photogenic authors.

The author shielding

Title keyword filtering

Solution ideas

The realization idea is actually very simple, first of all, it should be clear that the nuggets article list is the background request back to the data, in the future when the official implementation of this function can be directly returned to the back end after filtering the results, can also get the front end of the data for filtering processing and then display. We external plug-ins don’t have this capability and can only do this by hiding already generated page elements.

Console input code version

Let’s start with the simplest implementation, which filters all content that contains the keyword ‘CSS’. First, let’s open chrome’s debug panel and take a look at the HTML structure of the list of articles in the homepage:

As shown in the figure, all articles are in the form of LI tags in the UL of the class as entry-list. So what we need to do now is to select all LI tags in the entry-list and search for the contents inside. CSS will hide the Li tags if there are keywords.

/ / $$is supported by most browsers debug console API, equivalent to the document. QuerySelectorAll, cannot use at ordinary times development.$$,'.entry-list>li').forEach(item= > {
    //innerText is the "rendered" text content of a node and its descendants, which is case insensitive to CSS.
    if (/css/i.test(item.innerText)) {
        // Elements that meet the requirements are hidden
        item.hidden = true; }});Copy the code

Type this code into the console, and you’ll see that all articles containing CSS keywords (not just titles) are hidden from the list.

If you want to hide it, you have to run the code again on the console.

Oil monkey foot edition

What we want to do is a complete plug-in, rather than the above manual input code in the console, although you can write a complete plug-in code console run, in fact, the function is the same, but ultimately not a long-term solution.

We can first make this feature into a browser plug-in, such as creating projects based on Chrome’s specifications and apis, and finally package it into a CRX file that Chrome can install locally or, for a small fee, become a Google developer and publish it in the Google App Store.

The advantage of the browser plugin is that you can call more browser-level apis and use the related functions provided by the browser for the native plugin, but for the small functions we only provide for a website, some of the overkill, and this plugin only works in Chrome, if other browsers want to use it will have to be developed. FireFox, for example, uses an XPI format extension.

A better approach is to use Tampermonkey.

Tampermonkey is a browser plugin that is the most popular user script manager available in Chrome, Microsoft Edge, Safari, Opera Next, and Firefox. That means we can write scripts that run across browsers. And that’s what we’re doing today in the form of an oil monkey script.

More interested in browser plug-in development friends recommend this article: [goods] Chrome plug-in (extension) development overview

Plan implementation

Install Tampermonkey

Tampermonkey is easy to install, and not easy to install. Tampermonkey is listed in the official plugin market for all browsers, and is easily searchable and downloaded, not only for the aforementioned browsers, but also for some mobile browsers. Tampermonkey’s official website is not written, but some domestic browser plug-ins are available in the market, such as 360 Speed browser, Sogou browser. The reason is not simple, is that some browser plug-in market we are not easy to access.

If the browser plugin market really doesn’t have one, CRX native installations are usually supported as long as there is Chromium kernel.

After the installation, we can click the oil monkey plug-in icon on the browser and view, open, delete and manage our script in the management panel. We can find several websites of script market in obtaining new scripts and directly download scripts written by others.

Creating a script File

If we want to develop our own scripts, we can open a script writing page by adding new scripts, but it is recommended to write in our own editor, with prompts and formatting more convenient.

The script created by default has some code like this, which is a header like a comment. Here is a brief description of this, and more information can be found in the official documentation.

// ==UserScript==
// @name <- Plugin name, will be displayed in the admin panel
/ / @ namespace https://github.com/hoc2019/blackList < - script namespace
// @version 0.1 <- Version number
// @description <- description
// @author wangzy <- author
// @match https://juejin.im/* <- Only matched websites will use this script
/ / @ the require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js < - introducing the script, here in jQuery
// @grant GM_addStyle <- Method to inject CSS style to website
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...}) ();Copy the code

Article list filtering – MutationObserver observes dom node changes

Filtering hide for the article list works in much the same way as the console input code version above, with the emphasis on when to trigger the hide operation. Due to asynchronous data requests, filtering will fail if the article list DOM node is not created too early, and if it is too late, the list will disappear visually.

A simple and crude way to do this is to write a poll that continuously checks for the existence of the dom node in the list of articles. If the dom node exists, the article is loaded and ready for filtering. Similarly, if you scroll to the bottom to load an article, you can also use polling to detect changes in the number of DOM nodes in the article list to trigger a filtering operation (of course, you can also detect a scroll wheel event). But polling itself is a way to find a balance between performance and experience. If the polling interval is short, the judgment will be accurate and the experience will be good, but it will consume a lot of performance. Otherwise, the performance will be good and the experience will be poor.

Therefore, MutationObserver is used here. MutationObserver can be used to listen for changes of DOM nodes and is a substitute for the old Mutation Events function. For details, please refer to the DOCUMENTATION of MDN.

The general usage is as follows:

const container = $('#juejin');
const config = {
    attributes: false.// Check node attribute changes. This is not used here
    childList: true.// Detect the addition and deletion of child nodes
    subtree: true           // Detect nodes containing descendants
};
const mutationCallback = mutationsList= > {
    for (let mutation of mutationsList) {
        const type = mutation.type;
        const addedNodes = mutation.addedNodes;  // Add node array
        // Will trigger the corresponding event according to the above Settings
        switch (type) {
            case 'childList':
                // Since we want to judge the list load, we only need to handle the event when the node is added
                if (addedNodes.length > 0) {
                    list = $('.entry-list');
                    if (list.children().length) {
                        // Stop observing
                        loadObserver.disconnect();
                        // Filter
                        filter(list.children());
                        // Create a scroll and load the observation
                        createNodeListener(list[0], config, updateLoad); }}break; }}};// Create the first load observation
const loadObserver = createNodeListener(container[0], config, handleLoad);
// Define a factory function to create an observer.
function createNodeListener(node, config, mutationCallback) {
    const observer = new MutationObserver(mutationCallback);
    observer.observe(node, config);
    return observer;
}
Copy the code

Because it is every time the article list node changes to deal with, so the response is very timely, it is not likely to appear the item first show and then disappear, and it is passively triggered, not like polling, the background constantly access to query DOM node information.

You might wonder why you created a new observation in the middle, because the original observation was based on a very top-level container (a div node with the id juejin, which was the only one that existed at first), and some other DOM node changes outside of the article list also trigger events, So when we get the entry list container (ul class for entry list), we stop looking at the top container and look only at the entry list container, so that we can respond precisely to changes in the entry list node.

Observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe observe The jQuery node is converted to a native DOM node, such as $('#juejin')[0].Copy the code

Save blacklist data – localStorage Data access

Blacklist data is stored in localStorage, a way to keep data for a long time even when the page is closed. For details, see the MDN document.

It is easy to use, but note that localStorage can only store strings. If you want to store arrays and objects, you need to serialize them to JSON strings.

/ / store
const authorBlackList = ['four'.Small five ' '];
localStorage.setItem('authorBlackList'.JSON.stringify(authorBlackList));

/ / read
const authorBlackList = JSON.parse(localStorage.getItem('authorBlackList'));
Copy the code

After storing data, you can open the Application panel of browser debugging to view it. Since it is saved locally and the browser clears localStorage, data will be cleared, and data cannot be shared across browsers. It costs to store data on the server side, so let the official do it.

Blacklist management sidebar

This is also easy to implement, write HTML, using jQuery to create a node to insert the page, CSS style should be provided by the oil monkey script method GM_addStyle, as mentioned earlier.

It looks something like this:

/ / CSS styles
const myScriptStyle ='.black-sidebar{background:#000}'
GM_addStyle(myScriptStyle);

/ / HTML structure
const sidebarHtml = '<div class="black-sidebar"><ul><ul></div>';
$('body').append($(sidebarHtml));
Copy the code

Perhaps the only thing to note is that the black list in the sidebar can be added dynamically later, so the click event cannot be directly bound to the generated LI tag using jquery’s Click method, but must be bound to the parent UL using the ON method, just like the event delegate. These are some of the most important things you can do with jQuery.

Different page sidebar data synchronization – VisiBilityChange event listener

The blacklist sidebar is generated from the beginning, although the actions on the current page are updated to the sidebar, but we usually have multiple pages open during browsing, so how do we keep the data in sync with multiple page sidebars? Obviously it’s not practical to manipulate the DOM across browser tabs, but we can listen to a TAB switch, retrieve the data and update the list when the TAB is in the display state.

We listen through the VisibilityChange event, which you can refer to the MDN documentation for details.

document.addEventListener('visibilitychange'.function() {
    if (document.visibilityState === 'visible') {
        // Update sidebar data
        updateSidebarList();
        if (pathname === 'post') {
            // If it is a detail page, update the mask button statusupdateBtnState(); }}});Copy the code

The effect is as follows:

The above document.visibilityState is a read-only property indicating the state of the TAB, visible indicating that it is displayed, and hidden indicating that it is hidden. Therefore, the hidden TAB page can also be detected through this method, the mobile browser switches to background S, and the media sound continues to play. This event can be manually paused to solve the problem.Copy the code

Article details page masking button

This is also very simple, but there is an interesting point in doing this. I was going to create the same element as the share button above and add the same class name, but the style selector also contains data-V-xxx data (vue’s scoped feature to prevent CSS style contamination). They simply cloned a share button node and changed the content.

The last

The basic functions of this plug-in have been completed, but because the purpose of development is for self-use, there is no compatibility, there is no test under various circumstances, just run through the use of their own line.

In fact, there are still many points that can be optimized, such as adding and deleting blacklists now requires page refresh to take effect, some DOM operations can be reduced, code can be further abstract encapsulation, object-oriented and so on. So the purpose of this article is not to promote the plugin, but to simply share the script development process and the ideas for solving some of the problems in it. It’s enough to get someone interested in script development, which is the purpose of this series.

The script has also been put on the GreasyFork script market. You can directly search the blacklist of gold digging articles to download and use it. However, there is no update plan in the future. Let’s make our own food and clothing.

Script repository address Article backup repository address