Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(2 edits) (+2)

I know a lot of threads on this exist, but a simple-ish workaround I’ve found is using jquery in the browser console.

$('div[class="game_genre"]:contains("YOUR_DISLIKED_THING_HERE")').parent().parent().hide(); 

To break that down into its parts:

$('div[class="game_genre"]

Search for a div with class “game_genre”

:contains("YOUR_DISLIKED_THING_HERE")')

That contains a desired phrase

.parent().parent().hide();

Hide the Parent of this Div’s parent element from display.

If you want to filter multiple you need to copy out the entire section from div[class=... to HERE"); Including the trailing parenthesis ), but not the trailing single quote '. As an example:

$('div[class="game_genre"]:contains("Visual Novel"),div[class="game_genre"]:contains("Racing")').parent().parent().hide();

This could likely be easily converted into a Tamper/Grease/Whatever monkey script.

I should note - this is not a Permanent filter, and it is imperfect as it would need applied again when scrolling. It can only hide loaded elements and is not “Forward looking”. This workaround will update the existing page layout because the site was designed in a Responsive manner, but this is not preventing those elements from being fetched from the server in the first place, and is an entirely cosmetic solution that needs constantly re-applied as you scroll the page, or navigate the site.

The above is provided as-is with no expectation of support or assistance. If you choose to use this workaround solution onus is completely on the user for any problems or errors that may arise as a result.

(1 edit)

"simple" ;-)

Well, it is trivial to modify the page on client side. Based on the information the client has. The main genre is displayed, so yeah, that one could be filtered. Will not work for secondary genres (genres selected as a tag).

You can put your code in a bookmarklet, instead of using the console. Unfortunately the genre is not selectable by css, otherwise you could use a user style addon, as you could do to filter developers or items.

I made a tampermonkey script, if you want to test it. It is the whole other reply, for easy copy pasting. I made a similar one for filtering items and even have an unpublished extension, but quite a lack of interest, despite there being so many threads about filtering.

--

Oh, and that tampermonkey script sticks and it will filter out genres on search pages, but there is no button on a search page to deactivate it. You need to go to a browse page to remove the filtering. Hence the differently colored button. If you use display:none as a style, you will not even notice that the filter is on in endless scroll mode.

(+1)
// ==UserScript==
// @name         itch genre filter
// @namespace    nonamespace
// @version      1
// @description  itch genre filter tampermonkey
// @author       https://redonihunter.itch.io/
// @match        *://itch.io/*
// @run-at       document-end
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// ==/UserScript==
// setup: copy into tampermonkey, edit configs, save
// usage: click the buttons on browse pages genre to change appearance of genres
(function () {
    if (!document.querySelector('.game_cell')) { return; }
    // configuration start
    const genre_filter_style = '{opacity:0.05}'; // '{display:none}' '{opacity:0.05}'
    const button_color_x = 'Tomato';
    const button_color_o = 'BlanchedAlmond';
    // configuration end
    const genre_filter = document.head.appendChild(document.createElement('style'));
    genre_filter.innerText = '.gfs' + genre_filter_style;
    hook_buttons();
    const observer = new MutationObserver((mutationsList, observer) => {
        const temp = [];
        for (var i = 0, ii = mutationsList.length; i < ii; i++) {
            mutationsList[i].addedNodes.forEach((node) => {
                if (node?.classList?.contains('game_cell')) { temp.push(node); }
            });
        }
        if (temp?.length) { update_cell_styles(temp); }
    });
    observer.observe(document, { childList: true, subtree: true });
    update_cell_styles(document.getElementsByClassName('game_cell'));
    function do_not_ignore_here() {
        if (document.URL == 'https://itch.io/library/recommendations') { return false; }
        return (/^https\:\/\/.+\.itch\.io\/$/.test(document.URL) || document.URL.startsWith('https://itch.io/profile/') ||
            document.URL.startsWith('https://itch.io/my-collections') || document.URL.startsWith('https://itch.io/c/') ||
            document.URL.startsWith('https://itch.io/my-purchases') || document.URL.startsWith('https://itch.io/library') ||
            document.URL.startsWith('https://itch.io/my-feed'))
    }
    function update_cell_styles(cell_list) {
        if (do_not_ignore_here()) return;
        const ugly_string = GM_getValue('ugly_string', '');
        var genre;
        for (var i = 0, ii = cell_list.length; i < ii; i++) {
            genre = cell_list[i]?.querySelector("div.game_cell_data > div.game_genre")?.innerText;
            if (genre && ugly_string.includes(genre)) {
                cell_list[i].classList.add('gfs');
            } else {
                cell_list[i].classList.remove('gfs');
            }
        }
    }
    function hook_buttons() {
        const ul = document.querySelector('ul > li > a[href="/games/genre-action"')?.parentElement?.parentElement;
        if (!ul || document.getElementById('rfbid')) return;
        const reset_filter_button = document.createElement('button'); reset_filter_button.innerText = 'Reset Genre Filter';
        reset_filter_button.id = 'rfbid';
        ul.appendChild(reset_filter_button);
        reset_filter_button.addEventListener('click', () => {
            for (const li of ul.childNodes) {
                const btn = li.querySelector('.gfbxobtn');
                if (btn) {
                    btn.innerText = 'O';
                    btn.style.backgroundColor = button_color_o;
                }
            }
            GM_setValue('ugly_string', '');
            reset_filter_button.style.backgroundColor = button_color_o;
            update_cell_styles(document.getElementsByClassName('game_cell'));
        });
        const ugly_string = GM_getValue('ugly_string', '');
        reset_filter_button.style.backgroundColor = ugly_string ? button_color_x : button_color_o;
        for (const li of ul.childNodes) {
            const genre = li.querySelector('a')?.innerText;
            if (genre) {
                const btn = document.createElement('button'); btn.innerText = ugly_string.includes(genre) ? 'X' : 'O';
                btn.style.backgroundColor = ugly_string.includes(genre) ? button_color_x : button_color_o;
                btn.classList.add('gfbxobtn');
                li.appendChild(btn);
                btn.addEventListener('click', () => {
                    var us = GM_getValue('ugly_string', '');
                    if (us.includes(genre)) {
                        us = us.replace(genre, '')
                        btn.innerText = 'O';
                        btn.style.backgroundColor = button_color_o;
                    } else {
                        us += genre;
                        btn.innerText = 'X';
                        btn.style.backgroundColor = button_color_x;
                    }
                    reset_filter_button.style.backgroundColor = us ? button_color_x : button_color_o;
                    GM_setValue('ugly_string', us);
                    update_cell_styles(document.getElementsByClassName('game_cell'));
                });
            }
        }
    }
})();