Better favourite button (no more refreshing page)

Toggle favorites via fetch without following href making double navigation a problem

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        Better favourite button (no more refreshing page)
// @namespace   Violentmonkey Scripts
// @match       *://*.furaffinity.net/view/*
// @grant       none
// @version     1.0
// @license     MIT
// @author      crunchy2382
// @description Toggle favorites via fetch without following href making double navigation a problem
// @icon        https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png
// ==/UserScript==

(function () {
    'use strict';

    function findFavButton() {
        return [...document.querySelectorAll('a')].find(a => {
            const text = a.textContent.trim();
            return text === '+Fav' || text === '-Fav';
        });
    }

    function extractFavButtonFromHTML(html) {
        const doc = new DOMParser().parseFromString(html, 'text/html');
        return [...doc.querySelectorAll('a')].find(a => {
            const t = a.textContent.trim();
            return t === '+Fav' || t === '-Fav';
        });
    }

    function bindButton(button) {
        if (button.dataset.favBound) return;
        button.dataset.favBound = 'true';

        const originalHref = button.href;
        if (!originalHref) return;

        button.dataset.favUrl = originalHref;
        button.removeAttribute('href');
        button.style.cursor = 'pointer';

        button.addEventListener('click', e => {
            e.preventDefault();
            e.stopPropagation();

            const url = button.dataset.favUrl;
            if (!url) return;

            button.style.pointerEvents = 'none';
            button.style.opacity = '0.6';

            fetch(url, {
                method: 'GET',
                credentials: 'same-origin'
            })
            .then(res => res.text())
            .then(html => {
                const newBtn = extractFavButtonFromHTML(html);
                if (!newBtn) return;

                button.textContent = newBtn.textContent.trim();
                button.dataset.favUrl = newBtn.href;
            })
            .finally(() => {
                button.style.pointerEvents = '';
                button.style.opacity = '';
            });
        });
    }

    function init() {
        const btn = findFavButton();
        if (btn) bindButton(btn);
    }

    init();

    new MutationObserver(init).observe(document.body, {
        childList: true,
        subtree: true
    });
})();