簡體   English   中英

如何在頁面加載時更改 HTML 內容

[英]How to change the HTML content as it's loading on the page

我在我們的網站上進行 A/B 測試,我的大部分工作都是在一個 JS 文件中進行的,該文件在呈現任何其他內容之前加載到頁面頂部,但在 jQuery 加載之后,有時會派上用場。

舉一個更改 H1 標簽的非常簡單的例子,我通常會在頭部注入一個樣式,將 H1 不透明度設置為 0,然后在 DOMContentLoaded 上,我將操作 H1 內容,然后將不透明度設置為 1。這樣做的原因是為了避免在更改發生之前舊內容的閃現 - 隱藏整個對象在眼睛上更優雅。

我已經開始研究 MutationObserver API。 我之前在更改用戶可以打開的覆蓋對話框中的內容時使用過這個,這似乎是一種很酷的方法,我想知道是否有人設法使用 MutationObserver 來收聽文檔,因為它是第一次加載/在首次渲染之前和 DOMContentLoaded 之前解析和更改文檔?

這種方法可以讓我更改 H1 內容,而不必隱藏、更改它,然后顯示它。

我已經嘗試過但到目前為止都失敗了,並且剛剛閱讀了有關即將過時的突變事件並想知道我是否正在嘗試做一些不可能的事情。 然而,我們(不是我)已經設法在火星上放置了一個機器人,所以我希望我能解決這個問題。

那么是否可以在加載/解析頁面時使用 MutationObservers 即時更改 HTML 內容?

感謝您的任何幫助或任何指示。

問候, 尼克

MDN 上的文檔有一個通用的不完整示例,沒有展示常見的陷阱。 變異摘要庫提供了一個人性化的包裝器,但與所有包裝器一樣,它增加了開銷。 請參閱MutationObserver 的性能以檢測整個 DOM 中的節點

創建並啟動觀察者。

讓我們使用遞歸文檔范圍的 MutationObserver 來報告所有添加/刪除的節點。

var observer = new MutationObserver(onMutation);
observer.observe(document, {
  childList: true, // report added/removed nodes
  subtree: true,   // observe any descendant elements
});

添加節點的簡單枚舉。

減慢極大/復雜頁面的加載速度,請參閱性能
有時會錯過在父容器中合並的 H1 元素,請參閱下一節。

function onMutation(mutations) {
  mutations.forEach(mutation, m => {
    [...m.addedNodes]
      .filter(node =>
        node.localName === 'h1' && /foo/.test(node.textContent))
      .forEach(h1 => {
        h1.innerHTML = h1.innerHTML.replace(/foo/, 'bar');
      });
  });
}

添加節點的有效枚舉。

現在是困難的部分。 當頁面正在加載時,突變記錄中的節點可能是容器(就像整個站點標題塊,其所有元素都報告為一個添加的節點):規范不要求單獨列出每個添加的節點,因此我們必須使用querySelectorAll (非常慢)或getElementsByTagName (非常快)查看每個元素的內部。

function onMutation(mutations) {
  for (var i = 0, len = mutations.length; i < len; i++) {
    var added = mutations[i].addedNodes;
    for (var j = 0, node; (node = added[j]); j++) {
      if (node.localName === 'h1') {
        if (/foo/.test(node.textContent)) {
          replaceText(node);
        }
      } else if (node.firstElementChild) {
        for (const h1 of node.getElementsByTagName('h1')) {
          if (/foo/.test(h1.textContent)) {
            replaceText(h1);
          }
        }
      }
    }
  }
}

function replaceText(el) {
  const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
  for (let node; (node = walker.nextNode());) {
    const text = node.nodeValue;
    const newText = text.replace(/foo/, 'bar');
    if (text !== newText) {
      node.nodeValue = newText;
    }
  }
}

為什么有兩個丑陋的香草for循環? 因為forEachfilter以及 ES2015 for (val of array)在某些瀏覽器中可能會非常慢,請參閱MutationObserver 的性能以檢測整個 DOM 中的節點

為什么是樹行者 保留附加到子元素的任何事件偵聽器。 僅更改Text節點:它們沒有子節點,更改它們不會觸發新的變化,因為我們使用了childList: true ,而不是characterData: true

通過實時 HTMLCollection 處理相對稀有的元素,而無需枚舉突變。

因此,我們尋找一個應該很少使用的元素,如 H1 標簽或 IFRAME 等。在這種情況下,我們可以使用 getElementsByTagName 返回的自動更新的 HTMLCollection 來簡化和加速觀察者回調。

const h1s = document.getElementsByTagName('h1');

function onMutation(mutations) {
  if (mutations.length === 1) {
    // optimize the most frequent scenario: one element is added/removed
    const added = mutations[0].addedNodes[0];
    if (!added || (added.localName !== 'h1' && !added.firstElementChild)) {
      // so nothing was added or non-H1 with no child elements
      return;
    }
  }
  // H1 is supposed to be used rarely so there'll be just a few elements
  for (var i = 0, h1; (h1 = h1s[i]); i++) {
    if (/foo/.test(h1.textContent)) {
      // reusing replaceText from the above fragment of code 
      replaceText(h1);
    }
  }
}

我以 A/B 測試為生,我經常使用 MutationObservers,結果很好,但更多時候我只是做長輪詢,這實際上是大多數 3rd 方平台在使用他們的 WYSIWYG(或有時甚至他們的代碼編輯器)。 50 毫秒的循環不應減慢頁面速度或導致 FOUC。

我通常使用一個簡單的模式,如:

var poller = setInterval(function(){
  if(document.querySelector('#question-header') !== null) {
    clearInterval(poller);

    //Do something
  }
}, 50);

您可以使用 sizzle 選擇器獲取任何 DOM 元素,就像在 jQuery 中使用 document.querySelector 一樣,這有時是您唯一需要庫的東西。

事實上,我們在我的工作中經常這樣做,我們有一個構建過程和一個模塊庫,其中包括一個名為When的函數,它完全符合您的要求。 這個特定的函數會檢查 jQuery 和元素,但是修改庫而不依賴於 jQuery 是微不足道的(我們依賴 jQuery,因為它在我們客戶的大多數站點上,我們將它用於很多東西)。

說到第 3 方測試平台和 javascript 庫,根據實施情況,許多平台(如 Optimizely、Qubit 和我認為 Monetate)捆綁了一個 jQuery 版本(有時被精簡),在執行代碼時立即可用,因此如果您使用的是第 3 方平台,則需要考慮這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM