6

Я делаю A/B Тестирование на нашем сайте, и я выполняю большую часть своей работы в файле JS, который загружается вверху страницы, прежде чем что-либо будет отображаться но после загрузки jQuery, который пригодится время от времени.Как изменить содержимое HTML по мере его загрузки на страницу

Принимая очень простой пример изменения тега H1, я бы обычно вводил стиль в голове, чтобы установить непрозрачность H1 в 0, а затем в DOMContentLoaded, я бы манипулировал содержимым H1, а затем устанавливал непрозрачность на 1. Причина этого заключается в том, чтобы избежать вспышки старого контента до того, как произойдёт изменение - скрытие всего объекта более грациозно на глазу.

Я начал искать API MutationObserver. Я использовал это раньше, когда меняю содержимое в диалоговом окне оверлея, которое пользователь может открыть, что кажется довольно крутым подходом, и мне интересно, сумел ли кто-нибудь использовать MutationObserver для прослушивания документа, поскольку он сначала загружает/синтаксический анализ и внесение изменений в документ перед первым рендерингом и перед DOMContentLoaded?

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

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

Возможно ли использовать MutationObservers для изменения содержимого HTML на лету при загрузке страницы/анализе?

Спасибо за любую помощь или любые указатели.

С уважением, Ник

+0

Hello @wOxxOm - Во-первых, извинения за то, что вы вызвали волнение в воскресенье, но спасибо за ваш ответ. Во-вторых, не могли бы вы поделиться тем, что конкретно вас беспокоит? В-третьих, по шкале от одного до десяти, как вы волновались? И, наконец, если у вас есть подходящий ресурс (ы) из одной из тонны легкодоступных примеров, возможно, вы могли бы поделиться им как с ответом, и если это будет правильно, я буду отмечать его так. Спасибо за вашу помощь. –

+0

Мне было бы интересно услышать ответ на этот вопрос, я смотрел на это не так давно, но в итоге остановил его, так как у него не было времени или необходимости его завершить. – osouthgate

+0

Спасибо за отзыв @wOxxOm - очень полезно, хотя ни одна из ссылок на первых двух страницах не предоставила действительного ответа, однако есть хорошие статьи. Если вам известно о дублированном потоке в stackoverflow, мы можем связать этот вопрос с этим как действительный ответ, если он считается допустимым дубликатом.Извините за то, что тратил свое время, но вы можете прекратить отвечать и, возможно, сосредоточить свои усилия на других. Я желаю вам здоровья. –

ответ

9

The docs on MDN имеет общий неполный пример и не демонстрируют распространенные ошибки. Mutation summary библиотека обеспечивает удобную для пользователя обертку, но, как и все обертки, она добавляет лишние накладные расходы. См. Performance of MutationObserver to detect nodes in entire DOM.

Создайте и запустите наблюдателя.

Давайте используем рекурсивный MutationObserver для документов, который сообщает все добавленные/удаленные узлы.

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

Наивные перечисление добавленных узлов.

Замедляет загрузку чрезвычайно больших/сложных страниц, см. Performance.
Иногда пропускает элементы H1, объединенные в родительский контейнер, см. Следующий раздел.

function onMutation(mutations) { 
    mutations.forEach(mutation, mutation => 
     Array.prototype 
      .filter.call(mutation.addedNodes, added => 
       added.localName == 'h1' && added.textContent.match(/foo/) 
      ).forEach(h1 => 
       h1.innerHTML = h1.innerHTML.replace(/foo/, 'bar') 
      ); 
    ); 
} 

Эффективное перечисление добавленных узлов.

Теперь трудная часть. Узлы в записи мутации могут быть контейнерами во время загрузки страницы (например, весь блок заголовка сайта со всеми его элементами, указанными как только один добавленный узел): спецификация doesn't require каждый добавленный узел должен быть указан отдельно, поэтому нам придется загляните внутрь каждого элемента, используя querySelectorAll (очень медленно) или getElementsByTagName (очень быстро).

function onMutation(mutations) { 
    for (var i = 0, len = mutations.length; i < len; i++) { 
     var added = mutations[i].addedNodes; 
     for (var j = 0, lenAdded = added.length; j < lenAdded; j++) { 
      var node = added[j]; 
      var found; 
      if (node.localName === 'h1') { 
       found = [node]; 
      } else if (node.children && node.children.length) { 
       found = node.getElementsByTagName('h1'); 
      } else { 
       continue; 
      } 
      for (var k = 0, lenFound = found.length; k < lenFound; k++) { 
       var h1 = found[k]; 
       if (!h1.parentNode || !h1.textContent.match(/foo/)) { 
        // either removed in another mutation or has no text to replace 
        continue; 
       } 
       var walker = document.createTreeWalker(h1, NodeFilter.SHOW_TEXT); 
       while (walker.nextNode()) { 
        var textNode = walker.currentNode; 
        var text = textNode.nodeValue; 
        if (text.match(/foo/)) { 
         textNode.nodeValue = text.replace(/foo/, 'bar'); 
        } 
       } 
      } 
     } 
    } 
} 

Почему уродливая ваниль for петли? Потому что forEach и filter и ES2015 for (val of array) очень медленны в сравнении. См. Performance of MutationObserver to detect nodes in entire DOM.

Почему TreeWalker? Чтобы сохранить прослушиватели событий, прикрепленные к подэлементам. Чтобы изменить только узлы Text: у них нет дочерних узлов, и их изменение не вызывает новую мутацию, потому что мы использовали childList: true, а не characterData: true.

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

Итак, мы ищем элемент, который должен использоваться редко, как тег H1, или IFRAME и т. Д. В этом случае мы можем упростить и ускорить обратный вызов наблюдателя с автоматически обновляемым HTMLCollection, возвращаемым getElementsByTagName.

var h1s = document.getElementsByTagName('h1'); 
function onMutation(mutations) { 
    if (mutations.length == 1) { 
     // optimize the most frequent scenario: one element is added/removed 
     var added = mutations[0].addedNodes[0]; 
     if (!added || (added.localName !== 'h1' && !added.children.length)) { 
      // 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, len = h1s.length; i < len; i++) { 
     var h1 = h1s[i]; 
     if (!h1.textContent.match(/foo/)) { 
      continue; 
     } 
     var walker = document.createTreeWalker(h1, NodeFilter.SHOW_TEXT); 
     while (walker.nextNode()) { 
      var textNode = walker.currentNode; 
      var text = textNode.nodeValue; 
      if (text.match(/foo/)) { 
       textNode.nodeValue = text.replace(/foo/, 'bar'); 
      } 
     } 
    } 
} 
+0

Ницца, спасибо. Я проведу это вечером. Хорошая вещь. –

+0

Привет, еще раз спасибо за этот ответ ... Я пытаюсь разобраться с использованием TreeWalker - вы сказали, что это чтобы сохранить прослушиватели событий, прикрепленные к подэлементам тега h1, и поэтому он не запускает новая мутация, но я ошибаюсь в мышлении, изменяя значение текстового узла без TreeWalker, но также не запускает новую мутацию из-за использованных фильтров и не вмешивается в какие-либо обработчики событий - мы просто меняем textNode.nodeValue? Приветствия –

+0

Попробуйте угадать, какие дочерний узел правильного текст узел без перечисления их рекурсивно: 1) '

первого второго третьего

' и 2) '

первого второго третьего

' – wOxxOm

1

я тестирование A/B для жизни, и я использую MutationObservers довольно часто с хорошими результатами, но гораздо чаще я просто долго опроса, который на самом деле то, что большинство из 3-й партии платформ делать под капот, когда вы используете их WYSIWYG (или иногда даже их редакторы кода). Петля длиной 50 миллисекунд не должна замедлять страницу или вызывать FOUC.

Я обычно использую простой шаблон, как:

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

    //Do something 
    } 
}, 50); 

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

На самом деле мы делаем это так часто в своей работе, что у нас есть процесс сборки и a module library, который включает в себя функцию под названием Когда, которая делает именно то, что вы ищете. Эта конкретная функция проверяет как jQuery, так и элемент, но было бы тривиально модифицировать библиотеку, чтобы не полагаться на jQuery (мы полагаемся на jQuery, так как он находится на самых сайтов наших клиентов, и мы используем его для множества вещей) ,

Говоря о сторонних платформах тестирования и javascript-библиотеках, в зависимости от реализации множество платформ (например, Optimizely, Qubit, и я думаю, Monetate) связывают версию jQuery (когда-то сокращенную), которая доступна сразу при выполнении вашего кода, так что вам нужно изучить, если вы используете стороннюю платформу.

+0

Спасибо за это @Beau. Я использовал этот шаблон раньше, но иногда он не был пуленепробиваемым. Я дам ему еще один шаг в этой ситуации и отчитаюсь, однако, я думаю, что я начинаю предпочитать использовать естественные браузерные события, даже если это немного уменьшает целевую аудиторию. (jQuery. Мы не объединяемся с нашим фрагментом, мы загрузили его заранее). Приветствия. –

+0

FYI 50ms - это 3 кадра на 60fps, используемых браузерами для рисования страниц. Это огромный пробел (на самом деле заметна разница в кадрах). И есть еще большая проблема: обратный вызов таймера может задерживаться случайным образом при загрузке сложной страницы с интенсивным использованием процессора (я видел задержки 500 мс). – wOxxOm

Смежные вопросы