Документ.querySelector возвращает значение null, пока элемент не будет проверен с помощью DevTools

#javascript #html #google-chrome #dom

#javascript #HTML #google-chrome #dom

Вопрос:

Я пытаюсь создать расширение Chrome, которое находит «спонсируемые» сообщения на Facebook и удаляет их.

Делая это, я заметил это довольно странное поведение Google Chrome на Facebook.com , где определенные типы запросов для существующих элементов (в моем случае document.querySelector('a[href*="/ads/about"]'); ) будут возвращать null . Но если вы нажмете на них «проверить» (с помощью инструмента проверки или CTRL SHIFT C), они отобразятся в DevTools, а затем снова запустите запрос в консоли, чтобы показать элемент. Без какой-либо прокрутки, перемещения, изменения размера или каких-либо действий со страницей.

Это можно легко воспроизвести, используя приведенные выше инструкции, но для наглядности я сделал следующее видео, которое точно показывает странное поведение:

https://streamable.com/mxsf86

Это какая-то проблема с кэшированием dom-запросов? Вы когда-нибудь сталкивались с чем-то подобным? Спасибо

РЕДАКТИРОВАТЬ: проблема теперь сводится к возврату запроса null до тех пор, пока элемент не будет завис, и это больше не проблема, связанная с DevTools.

Комментарии:

1. » и затем повторный запуск запроса в консоли покажет элемент. » похоже, что при первом запуске кода элемент просто отсутствует, и вам нужно дождаться его добавления в DOM.

2. После просмотра видео: вы уверены , что ссылка не отображается при нажатии или более длительном наведении курсора мыши или что-то еще?

3. Возможно, div, который вы пытаетесь получить, создан с использованием порталов React и помещен в другое дерево DOM.

4. @VLAZ Я запускаю запрос примерно через 3-4 секунды после фактического времени, когда я вижу его на странице… итак, как он мог «еще не быть добавлен в DOM»?

5. Sponsored Это role="button" с tabindex 0, который перезагружает содержимое при нажатии и наведении курсора мыши. Вы также можете увидеть это на network вкладке. a Просто не существует до этого. Не имеет значения, наведете ли вы на него курсор с помощью DevTools open или нет.

Ответ №1:

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

Итак, если у вас есть задание найти рекламные ссылки, вам нужно будет выполнить следующее

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

Примечание: рекламные ссылки оплачиваются компаниями, и они были бы не очень довольны, если бы их рекламные слоты использовались незаинтересованными ботами.

Ответ №2:

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

 // using an IIFE ("Immediately-Invoked Function Expression"):
(function() {
    'use strict';

// using Arrow function syntax to define the callback function
// supplied to the (later-created) mutation observer, with
// two arguments (supplied automatically by that mutation
// observer), the first 'mutationList' is an Array of
// MutationRecord Objects that list the changes that were
// observed, and the second is the observer that observed
// the change:
const nodeRemoval = (mutationList, observer) => {

  // here we use Array.prototype.forEach() to iterate over the
  // Array of MutationRecord Objects, using an Arrow function
  // in which we refer to the current MutationRecord of the
  // Array over which we're iterating as 'mutation':
  mutationList.forEach( (mutation) => {

    // if the mutation.addedNodes property exists and
    // also has a non-falsy length (zero is falsey, numbers
    // above zero are truthy and negative numbers - while truthy -
    // seem invalid in the length property):
    if (mutation.addedNodes amp;amp; mutation.addedNodes.length) {

        // here we retrieve a list of nodes that have the
        // "aria-label" attribute-value equal to 'Advertiser link':
        mutation.target.querySelectorAll('[aria-label="Advertiser link"]')
          // we use NodeList.prototype.forEach() to iterate over
          // the returned list of nodes (if any) and use (another)
          // Arrow function:
          .forEach(
            // here we pass a reference to the current Node of the
            // NodeList we're iterating over, and use
            // ChildNode.remove() to remove each of the nodes:
            (adLink) => adLink.remove() );
    }
  });
},
      // here we retrieve the <body> element (since I can't find
      // any element with a predictable class or ID that will
      // consistently exist as an ancestor of the ad links):
      targetNode = document.querySelector('body'),

      // we define the types of changes we're looking for:
      options = {
          // we're looking for changes amongst the
          // element's descendants:
          childList: true,
          // we're not looking for attribute-changes:
          attributes: false,
          (if this is false, or absent, we look only to
          changes/mutations on the target element itself):
          subtree: true
},
      // here we create a new MutationObserver, and supply
      // the name of the callback function:
      observer = new MutationObserver(nodeRemoval);

    // here we specify what the created MutationObserver
    // should observe, supplying the targetNode (<body>)
    // and the defined options:
    observer.observe(targetNode, options);

})();
  

Я понимаю, что в вашем вопросе вы ищете элементы, которые соответствуют другому атрибуту и значению атрибута ( document.querySelector('a[href*="/ads/about"]') ), но поскольку это значение атрибута не соответствует моей собственной ситуации, я не мог использовать его в своем коде, но это должно быть так же просто, как замена:

 mutation.target.querySelectorAll('[aria-label="Advertiser link"]')
  

С:

 mutation.target.querySelector('a[href*="/ads/about"]')
  

Хотя стоит отметить, что querySelector() вернет только первый узел, соответствующий селектору, или null ; поэтому вам может потребоваться включить некоторые проверки в ваш код.

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

 (function() {
    'use strict';

const nodeRemoval = (mutationList, observer) => {
  mutationList.forEach( (mutation) => {
    if (mutation.addedNodes amp;amp; mutation.addedNodes.length) {
        mutation.target.querySelectorAll('[aria-label="Advertiser link"]').forEach( (adLink) => adLink.remove() );
    }
  });
},
      targetNode = document.querySelector('body'),
      options = {
          childList: true,
          attributes: false,
          subtree: true
},
      observer = new MutationObserver(nodeRemoval);

    observer.observe(targetNode, options);

})();
  

Ссылки:

Ответ №3:

Я столкнулся с той же проблемой в Chrome. Если это кому-нибудь поможет, я решил это, обратившись к фрейму с помощью

 window.frames["myframeID"].document.getElementById("myElementID")