Рекурсивно и асинхронно проверять, существует ли элемент html - PullRequest
0 голосов
/ 07 февраля 2020

Я взаимодействую с веб-страницей, на которой некоторые из более глубоких вложенных элементов в подкадрах загружаются даже после того, как документ готов - т.е. они полагаются на ожидающие запросы ajax / api, которые еще не завершены при загрузке документа.

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

setTimeout(function() {
      $("#better_content .binder__toc").append(
        "<h4>Experiments</h4><ul><li>This</li><li>is</li><li>a</li><li>test</li></ul>"
      );
    }, 5000);

Однако было бы неплохо рекурсивным и асинхронным (неблокирующим) способом продолжать проверять указанный элемент. ("#better_content .binder__to c") до тех пор, пока значение undefined или null не будет возвращено, т.е. элемент существует. Я пытался сделать это с помощью обещаний. Простой пример со счетчиком выглядит следующим образом:

static waitForElement = counter => {

   counter++

    return new Promise((res, rej) => {
      if (counter < 5) {
        this.waitForElement(counter)
          .then(function() {
            res("complete");
          })
          .catch(rej);
      }
      res("complete");
    });
  };

this.waitForElement(counter)
      .then(a => console.log(a))
      .catch(a => console.log(a));

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

static waitForElement = selector => {        

    let found = $(document).find(selector)[0];        
    console.log(found);

    return new Promise((res, rej) => {
      if (found === undefined) {
        this.waitForElement(selector)
          .then(function() {
            res("found");
          })
          .catch(rej);
      }
      res("found");
    });
  };


this.waitForElement("#better_content .binder__toc")
      .then(a => {
        console.log(a);
        $("#better_content .binder__toc").append(
          "<h4>Experiments</h4><ul><li>This</li><li>is</li><li>a</li><li>test</li></ul>"
        );
      })
      .catch(a => console.log(a));

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

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

1 Ответ

0 голосов
/ 07 февраля 2020

Вы можете проверить наличие изменений в элементе или дочернем элементе с помощью Mutation Observer API. API запускает функцию обратного вызова, в которой вы можете отстаивать свою логику c, чтобы действовать, когда происходит определенное изменение в элементе. В вашем случае вы захотите прослушивать контейнер, к которому элемент добавляется с помощью запроса fetch.

Можно прослушивать определенные изменения, например, изменения атрибутов, или в вашем случае изменения в ребенок. В конфигурации добавьте childList: true, что будет означать, что вы хотите что-то делать, когда добавляется или удаляется дочерний элемент.

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

const target = document.getElementById('container');
const config = { childList: true };
const observer = new MutationObserver((mutations, observer) => {

  /**
   * Loop through all changes
   */
  mutations.forEach(mutation => {
    
    /**
     * Only act if it is a change in the children.
     */
    if (mutation.type !== 'childList') {
      return;
    }
  
    /**
     * Loop through the added elements and check if 
     * it is the correct element. Then add HTML to 
     * the newly added element.
     */
    mutation.addedNodes.forEach(node => {
      if (node.id === 'content') {
        const html = `
          <h4>Experiments</h4>
          <ul>
            <li>This</li>
            <li>is</li>
            <li>a</li
            ><li>test</li>
          </ul>`;
        node.innerHTML = html;
      }
    });
    
    // Stop observing.
    observer.disconnect();
    
  });
  
});

// Observe the children in container.
observer.observe(target, config);

// Add content element after 2 seconds.
setTimeout(function() {
  const content = document.createElement('div');
  content.id = 'content';
  target.append(content);
}, 2000);
<div id="container"></div>
...