Користувальницький скрипт, щоб зачекати, поки сторінка завантажиться, перед виконанням методів коду?


80

Я пишу сценарій користувача Greasemonkey і хочу, щоб конкретний код виконувався, коли сторінка повністю закінчує завантаження, оскільки вона повертає кількість div, яку я хочу відображати.

Проблема в тому, що ця конкретна сторінка іноді займає трохи часу, перш ніж все завантажується.

Я пробував, документи $(function() { });та $(window).load(function(){ });обгортки. Однак, здається, жоден не працює для мене, хоча я, можливо, застосовую їх неправильно.

Найкраще, що я можу зробити - це використовувати setTimeout(function() { }, 600);який працює, хоча він не завжди надійний.

Який найкращий прийом використовувати у Greasemonkey, щоб забезпечити виконання конкретного коду, коли сторінка закінчується завантаження?


3
Ви можете використовувати (function init(){var counter = document.getElementById('id-of-element');if (counter) { /* do something with counter element */ } else { setTimeout(init, 0);}})();для постійного опитування щодо існування елемента. Це найбільш загальне рішення.
Роб W

2
Двоюрідні брати Greasemonkey, Tampermonkey та Scriptish, підтримують більше @run-atцінностей, які включають document-idleі context-menuможуть бути корисними. Крім того , здається , що Greasemonkey є додавання підтримки для document-idleхоча вона не була документована досі.
Містер Лама,

@RobW, який працював на мене, дякую.
zx81

Відповіді:


71

Greasemonkey (зазвичай) не має jQuery. Тож загальним підходом є використання

window.addEventListener('load', function() {
    // your code here
}, false);

всередині вашого скрипта користувача


16
Додати jQuery просто, як і додати @require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.jsдо поточного сценарію. Я вклав їх у скрипт шаблону, тому що я використовую їх завжди. Також ставлять, this.$ = this.jQuery = jQuery.noConflict(true);щоб уникнути типових конфліктів.
m3nda

Додавання залежностей від jQuery коли
sleblanc

2
Не впевнені, чому люди так прагнуть додати jquery для всього.
Wyatt8740,

61

Це поширена проблема, і, як ви вже сказали, очікування завантаження сторінки недостатньо - оскільки AJAX може і змінює ситуацію ще довго після цього.

Для цих ситуацій існує стандартна (іш) надійна утиліта. це утилітаwaitForKeyElements() .

Використовуйте його так:

// ==UserScript==
// @name     _Wait for delayed or AJAX page load
// @include  http://YOUR_SERVER.COM/YOUR_PATH/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant    GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a major design
    change introduced in GM 1.0.
    It restores the sandbox.
*/

waitForKeyElements ("YOUR_jQUERY_SELECTOR", actionFunction);

function actionFunction (jNode) {
    //-- DO WHAT YOU WANT TO THE TARGETED ELEMENTS HERE.
    jNode.css ("background", "yellow"); // example
}

Вкажіть точні дані своєї цільової сторінки для більш конкретного прикладу.


Що робити, якщо на цільовій сторінці вже є JQuery? Завантаження JQuery на сторінку порушує вихідний код, але мій скрипт GM запускається до запуску JQuery. Конкретною сторінкою для мого сценарію є backpack.tf, на якій працює JQuery 1.7.2.
Джек

4
@Jack, Використання jQuery у сценарії не завантажує його на сторінку, а також нічого не порушує, коли ти використовуєш, @grant GM_addStyleяк показано. Ось для чого це там.
Brock Adams

обов’язково передайте trueяк третій аргумент, waitForKeyElementsякщо ви хочете, щоб ваш код запускався один раз
Starwarswii

41

Станом на Greasemonkey 3.6 (20 листопада 2015 р.) Ключ метаданих @run-atпідтримує нове значення document-idle. Просто вставте це в блок метаданих вашого сценарію Greasemonkey:

// @run-at      document-idle

Документація описує його наступним чином :

Скрипт запускатиметься після того, як сторінка буде завантажена, а всі ресурси (зображення, таблиці стилів тощо) завантажені та сценарії сторінки запущені.


7
Увага. Схоже, Greasemonkey реалізував це не так, як це зробили Chrome / Tampermonkey. Отже, сценарії можуть не працювати однаково в браузерах. Все-таки надійніше використовувати щось на зразок waitForKeyElementsабо MutationObserver.
Брок Адамс,

21

Відповідь Брока хороша, але я хотів би запропонувати ще одне рішення проблеми AJAX, яке є більш сучасним та елегантним. Оскільки його сценарій також використовує setInterval()для періодичної перевірки (300 мс), він не може негайно відповісти.

Якщо вам потрібно негайно відповісти, ви можете використати MutationObserver()для прослуховування змін DOM і відповіді на них, як тільки елемент буде створений

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

function check(changes, observer) {
    if(document.querySelector('#mySelector')) {
        observer.disconnect();
        // code
    }
}

Хоча відтоді check() пожежа відбувається при кожній зміні DOM, це може бути повільним, якщо DOM змінюється дуже часто або ваш стан займає багато часу для оцінки.

Інший варіант використання - якщо ви не шукаєте якийсь конкретний елемент, а просто чекаєте, поки сторінка перестане змінюватися. Ви можете поєднати це з setTimeout()тим, щоб теж зачекати.

var observer = new MutationObserver(resetTimer);
var timer = setTimeout(action, 3000, observer); // wait for the page to stay still for 3 seconds
observer.observe(document, {childList: true, subtree: true});

function resetTimer(changes, observer) {
    clearTimeout(timer);
    timer = setTimeout(action, 3000, observer);
}

function action(o) {
    o.disconnect();
    // code
}

Цей метод настільки універсальний, що ви також можете прослуховувати зміни атрибутів та тексту. Просто встановіть attributesі characterDataщоб trueв опціях

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

13

упаковка моїх сценаріїв $(window).load(function(){ })ніколи для мене не підводила.

можливо ваша сторінка закінчена, але все ще завантажується вміст ajax.

якщо це так, цей приємний фрагмент коду від Брока Адамса може вам допомогти:
https://gist.github.com/raw/2625891/waitForKeyElements.js

Зазвичай я використовую його для моніторингу елементів, які з’являються при зворотній передачі .

використовуйте його так: waitForKeyElements("elementtowaitfor", functiontocall)


6

Якщо ви хочете маніпулювати вузлами, як отримання значення вузлів або зміна стилю, ви можете почекати ці вузли, використовуючи цю функцію

const waitFor = (...selectors) => new Promise(resolve => {
    const delay = 500
    const f = () => {
        const elements = selectors.map(selector => document.querySelector(selector))
        if (elements.every(element => element != null)) {
            resolve(elements)
        } else {
            setTimeout(f, delay)
        }
    }
    f()
})

потім використовувати promise.then

// scripts don't manipulate nodes
waitFor('video', 'div.sbg', 'div.bbg').then(([video, loading, videoPanel])=>{
    console.log(video, loading, videoPanel)
    // scripts may manipulate these nodes
})

або використовувати async&await

//this semicolon is needed if none at end of previous line
;(async () => {
    // scripts don't manipulate nodes
    const [video, loading, videoPanel] = await waitFor('video','div.sbg','div.bbg')
    console.log(video, loading, video)
    // scripts may manipulate these nodes
})()

Ось приклад icourse163_enhance


2

Щоб виявити, чи XHR завершив завантаження на веб-сторінці, він запускає певну функцію. Я отримую це в розділі Як я використовую JavaScript для зберігання повідомлень "Завершення завантаження XHR" у консолі в Chrome? і це справді працює.

    //This overwrites every XHR object's open method with a new function that adds load and error listeners to the XHR request. When the request completes or errors out, the functions have access to the method and url variables that were used with the open method.
    //You can do something more useful with method and url than simply passing them into console.log if you wish.
    ///programming/43282885/how-do-i-use-javascript-to-store-xhr-finished-loading-messages-in-the-console
    (function() {
        var origOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function(method, url) {
            this.addEventListener('load', function() {
                console.log('XHR finished loading', method, url);
                display();
            });

            this.addEventListener('error', function() {
                console.log('XHR errored out', method, url);
            });
            origOpen.apply(this, arguments);
        };
    })();
    function display(){
        //codes to do something;
    }

Але якщо на сторінці багато XHR, я не уявляю, як відфільтрувати певний XHR.

Інший метод - waitForKeyElements (), що приємно. https://gist.github.com/BrockA/2625891
Є зразок для використання Greasemonkey. Запустити сценарій Greasemonkey на одній сторінці кілька разів?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.