Пользовательский скрипт работает только на страницах, обслуживаемых из бэкэнда, но не в интерфейсе SPA.

У меня есть следующий пользовательский скрипт, который я запускаю на Greasemonkey/Tampermonkey.

Я запускаю это на facebook.com которая обслуживает некоторые веб-страницы из бэкэнда, в начальной загрузке, а некоторые другие на лету, во внешнем интерфейсе, через HRO, так же, как это делает одностраничное приложение (SPA).

// ==UserScript==
// @name        facebook
// @namespace   nms
// @include     http://*.facebook.com/*
// @include     https://*.facebook.com/*
// @version     1
// @grant       none
// ==/UserScript==

setTimeout( () => {
    // generalStuff:
        document.querySelectorAll(' #left_nav_section_nodes, .fbChatSidebar ').forEach( (e)=> {
            e.style.visibility = "hidden";
        });

}, 1000);

Если я запускаю этот сценарий на консоли, даже на веб-страницах, основанных на HRO, он работает нормально, но при запуске из Greasemoneky/Tampermonkey он не будет работать на этих конкретных веб-страницах.

Как я мог заставить скрипт работать без проблем на SPA-подобных веб-страницах?

4 ответа

Решение

В таком случае, когда setTiemout, setInterval и делегирование события не работают сами по себе, можно поместить в память состояние, которое их реализует, а затем заменить им существующее состояние, чтобы содержимое DOM веб-страницы изменилось.

Вот код, используемый для замены данных, которые были загружены с AJAJ вместо непосредственно из PHP:

let utilityFunc = ()=> {
    var run = (run)=> {
       // insert your code here
    };

    var pS = window.history.pushState;
    var rS = window.history.replaceState;

    window.history.pushState = (a, b, url)=> {
        run(url);
        pS.apply(this, arguments);
    };

    window.history.replaceState = (a, b, url)=> {
        run(url);
        rS.apply(this, arguments);
    };

utilityFunc();

Вот что я понял из прочитанного здесь.

Я сам несколько раз сталкивался с этой проблемой, и в итоге я написал простую библиотеку, чтобы справиться с этим. Это позволяет мне запускать скрипт при изменении URL-адреса, определять URL-адреса с подстановочными знаками и ждать определенного элемента, если я хочу. Это называется спа-раннер .

      import { run } from "@banjoanton/spa-runner";


const handler = () => {
    console.log("hello world!");
}

const config = {
    urls: ["http://*.facebook.com/*"],
    runAtStart: true,
};

const unsubscribe = run(handler, config);

Для аналогичного варианта использования я использовал MutationObserver и проверилnode.baseURIкаждого узла в каждомmutation.addedNodes.

      (async function() {
    'use strict';
    const selector = '.quiz--answered';
    const observer = new MutationObserver(mutations => {
        if (!mutations.some(mutation => Array.from(mutation.addedNodes).some(node => node.baseURI.includes('/quiz/')))) {
            return;
        }
        const elm = document.querySelector(selector);
        if (elm) {
            // work with elm
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();

После прочтения этой документации этот код будет работать для изменения местоположения SPA:

      // ==UserScript==
...
// @match        https://subdomain.domain.tld/*
// @match        http://subdomain.domain.tld/*
// @grant        window.onurlchange
// ==/UserScript==

(function() {
    'use strict';
    if (window.onurlchange === null) {
        window.addEventListener('urlchange', (info) => {
            if (Object.values(info)[0].includes("/path/to/certainlocation")) {
                // your code here
            }

        });
    }
})();

Обратите внимание, что это не будет работать, если вы посетите определенное место напрямую, но это можно легко исправить, или вы можете создать другой пользовательский скрипт, который соответствует только этому определенному местоположению, например:

      // @match       https://subdomain.domain.tld/path/to/certainlocation
// @match       http://subdomain.domain.tld/path/to/certainlocation
Другие вопросы по тегам