"наблюдаю" в "MutationObserver": параметр 1 не относится к типу "Node"

Я создаю расширение для Chrome и пытаюсь добавить небольшой текст рядом с кнопкой "ОТПРАВИТЬ" в окне создания письма gMail.

Я использую MutationObserver, чтобы знать, когда появляется окно создания блока. Я делаю это, наблюдая за элементом с классом no поскольку элемент compose box создается как дочерний элемент этого элемента (класс no).

Когда пользователь нажимает кнопку "Создать" и появляется окно "Создать", я помещаю элемент рядом с кнопкой "Отправить", используя .after() метод. Имя класса кнопки ОТПРАВИТЬ .gU.Up,

Это настоящие имена классов gMail и тоже довольно странные.

Ниже приведен код, который я использую:

var composeObserver = new MutationObserver(function(mutations){ 
    mutations.forEach(function(mutation){
        mutation.addedNodes.forEach(function(node){
            $(".gU.Up").after("<td> <div> Hi </div> </td>");
        });
    });
});

var composeBox = document.querySelectorAll(".no")[2];
var config = {childList: true};
composeObserver.observe(composeBox,config);

Проблема в том, что я постоянно получаю следующую ошибку:

Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'

Кто-нибудь может помочь? Я перепробовал несколько вещей, а также посмотрел другие ответы здесь, но все еще не могу избавиться от этой ошибки.

Вот мой файл manifest.json:

{
    "manifest_version": 2,
    "name": "Gmail Extension",
    "version": "1.0",

    "browser_action": {
        "default_icon": "icon19.png",   
        "default_title": "Sales Analytics Sellulose"    
    },

    "background": {
        "scripts": ["eventPage.js"],
        "persistent": false
    },

    "content_scripts": [
    {
        "matches": ["https://mail.google.com/*"],
        "js": ["jquery-3.1.1.js", "insQ.min.js", "gmail_cs.js"]
    }
],

    "web_accessible_resources":[
        "compose_icon.png",
        "sellulosebar_icon.png" 
    ]
}

PS Я уже пробовал вставку библиотеки запросов, но у нее есть несколько недостатков. Это не позволяет мне быть конкретным относительно изменений в конкретном элементе. Мне еще предстоит попробовать библиотеку mutationsummary, но, поскольку она использует MutationObserver, я решил, что проблема не исчезнет.

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

Тем не менее, когда я добавляю console.log для выбранного элемента он отображается как неопределенный. Это означает, что вы, вероятно, правы в отношении выполнения кода до появления узлов. Можете ли вы сказать мне, как сделать так, чтобы задержка произошла? будет работать setTimeout? Как это работает в случае MutationObserver?

4 ответа

Решение

Как я уже упоминал в комментарии, и Ксан дал ответ, ошибка проясняет, что результат document.querySelectorAll(".no")[2] не оценивает узел.

Из информации, предоставленной вами в комментарии, становится ясно, что проблема в том, что узел, который вы хотите наблюдать, не существует, когда ваш код выполняется. Есть много способов отложить выполнение вашего кода до тех пор, пока этот узел не станет доступен. Некоторые возможности:

  1. Использование цикла setTimeout для опроса, пока вы не обнаружите, что элемент, на который вы хотите поместить MutationObserver, доступен:

    function addObserverIfDesiredNodeAvailable() {
        var composeBox = document.querySelectorAll(".no")[2];
        if(!composeBox) {
            //The node we need does not exist yet.
            //Wait 500ms and try again
            window.setTimeout(addObserverIfDesiredNodeAvailable,500);
            return;
        }
        var config = {childList: true};
        composeObserver.observe(composeBox,config);
    }
    addObserverIfDesiredNodeAvailable();
    

    Это найдет соответствующий узел относительно скоро после того, как он существует в DOM. Жизнеспособность этого метода зависит от того, как долго после вставки целевого узла вам нужно разместить наблюдателя на нем. Очевидно, что вы можете настроить задержку между попытками опроса в зависимости от ваших потребностей.

  2. Создайте еще один MutationObserver, чтобы наблюдать узел-предок выше в DOM для вставки узла, на котором вы хотите разместить своего основного наблюдателя. Несмотря на то, что он найдет соответствующий узел сразу после его вставки, он может быть достаточно ресурсоемким (ЦП), в зависимости от того, насколько высоко в DOM вы должны наблюдать, и насколько активны изменения DOM.

Попробуйте использовать это с JQuery.

Если вы разрабатываете расширение для Chrome и получаете HTML-элемент (Node) со страницы или DOM в content_script, то вы получите Object, и Node будет возвращен как свойство объекта. Затем вы можете получить узел от объекта, чтобы перейти в observe(Node,config) метод.

пример

var node = $("#elementId");  //this is wrong because if you logged this in the console you will get Object

var node = $("#elementId")[0];  //This gives the first property of the Object returned, and this is correct because if you logged this in the console you will get the Node element for which you want to detect changes in the DOM.

Эта ошибка означает, что document.querySelectorAll(".no")[2] это не Node,

Скорее всего, это означает, что такого элемента нет; querySelectorAll всегда будет возвращать NodeListдаже если он пуст; доступ к несуществующим членам списка завершается успешно без ошибки времени выполнения, но возвращается undefined: в этом смысле, NodeList действует как массив.

"Подождите, но это так! Я запускаю этот код в консоли, и он работает!" Вы можете резко воскликнуть. Это потому, что во время его выполнения, долгое время после того, как документ заканчивает загрузку, эти элементы существуют.

Итак, вам нужно дождаться добавления этого корневого элемента; скорее всего, с другим MutationObserver сделать работу.

Если вы используете jQuery, поместите этот код в

$(document).ready(function(){ // your code });

Используйте setInterval для обновления и проверки существования узла примерно каждую секунду. Как только узел станет доступен в DOM, вызовите функцию наблюдателя.

// target selector
const selector = 'div.watchlist-body';
// max time to wait (in seconds)
const maxSeconds = 10;
// time spend (in seconds)
let timer = 1;

// refresh every second
const refreshId = setInterval(() => {
  // check if dom exists
  if (document.querySelector(selector)) {
    // clear interval
    clearInterval(refreshId);
    // call observer
    observer();
  }
  // check if timeout
  if (timer++ >= maxSeconds) {
    clearInterval(refreshId);
    console.log('Error: Max allowed seconds exceeds.');
  }
}, 1000);

function observer() {
  console.log('Hello, I am from observer!');
}

Вместо того, чтобы делать бесконечное обновление подходящим maxSeconds, поэтому обновление завершится через определенные секунды. Управляйте своей логикой в ​​наблюдателе:)

Другие вопросы по тегам