2016-08-17 2 views
0

Чтобы поддерживать правильное и высокочувствительное наложение графического интерфейса на каждом веб-сайте, мне необходимо как можно скорее зарегистрировать и проанализировать все соответствующие элементы DOM. В настоящее время я использую MutationObserver, который делает эту работу для меня и упрощены, это выглядит следующим образом:Javascript: Регистрация * каждый * элемент DOM при создании/добавлении к DOM

var observer = new MutationObserver(
    function(mutations){ 
     mutations.forEach(
      function(mutation){ 
       if(mutation.type == 'childList') 
       { 
        var nodes = mutation.addedNodes; 
        var n = nodes.length; 

        for(var i = 0; i < n; i++) 
        { 
         if(nodes[i].nodeType == 1) // ELEMENT_NODE 
         { 
          // Save it in specific Array or something like this 
          AnalyzeNode(nodes[i]); 
         } 

        } 
       } 
      } 
     ); 
    } 
); 

var config = {subtree: true, childList: true}; 

observer.observe(document, config); 

Но я пришел к осознанию того, что используется MutationObserver не вызывая AnalyzeNode для каждый узел, содержащийся в DOM. Когда уже завершенное (вспомогательное) дерево создается вне DOM (например, путем выполнения внешнего скрипта JS при загрузке страницы), и вы добавляете его корень в DOM mutation.addedNodes будет содержать только корень поддерева и все его дочерние элементы будет оставаться незамеченным (потому что никаких дальнейших мутаций там не будет), будучи частью DOM, но не анализировалось.

У меня была идея проверки, если добавляется узел может уже иметь ChildNodes, чтобы определить его как корень литерой поддерева, но, к сожалению, кажется, что каждый addedNode может иметь детей в данный момент называются функции в MutationObserver в , Таким образом, на этом пути невозможно провести различие.

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

Кроме того, я думал о наборе узлов, чьи дети должны анализироваться вне вызова MutationObserver. Если добавленный узел имеет дочерние элементы при добавлении к DOM, узел добавляется в Set. Когда другая мутация происходит и один из его детей является частью addedNodes, его ребенок удаляет его родителя из набора с помощью mutation.target - что родительский узел (mutation.type должен быть childList). Проблема с этим подходом - это время, когда нужно проверять дочерние узлы в Set (и тот факт, что я мог запросить document.getElementsByTagname для каждого соответствующего типа элемента вместо сохранения набора, но проблема синхронизации все еще существует). Имейте в виду, что это должно быть как можно скорее, чтобы накладывать отзывчивость и установку на веб-сайт. Сочетание документа «ы onreadystatechange и добавления новых сценария узлов в DOM (в качестве индикатора, когда внешний код JS выполняется), может работать даже для сайтов, воссоздавая части его содержания (я смотрю на вас DuckDuckGo страница результатов поиска). Но это похоже на обходное решение, которое не решит проблему в 100% случаев.

Итак, есть ли другой, более эффективный способ? Или любой из этих подходов может быть достаточным, если его немного изменить? Большое спасибо!

(Пожалуйста, постарайтесь избежать JQuery, где это возможно, как пример кода, спасибо И, кстати, я использую CEF, поэтому в лучшем случае будет решение работать с Webkit/Blink.)

EDIT1: Отрисовка веб-сайта выполняется CEF и рендеринг GUI осуществляется C++/OpenGL с информацией, полученной указанным кодом Javascript.

+0

Посмотрите на это: https://davidwalsh.name/detect-node-insertion - похоже, делает то, что вы хотите сделать, без обхода узла – eithed

+0

Возможно, вы можете просто добавить глобальное правило '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ', ' – wOxxOm

+0

@eithedog Я видел это сообщение раньше, но я думаю, что было бы необходимо добавить слушателей к каждому добавленному узлу, поэтому снова: много дополнительных (а также нескольких) вызовов на узел. – dm102

ответ

1

Кажется, что ваша фактическая цель заключается в том, чтобы макет обнаруживал изменения в визуализированном выходе, а не (потенциально невидимые) изменения DOM.

В браузерах, основанных на gecko, вы можете использовать MozAfterPaint, чтобы получать уведомления о ограничивающих прямоугольниках с измененными областями, что довольно точно, но имеет несколько пробелов, таких как воспроизведение видео (которое изменяет отображаемый контент, но не макет) или асинхронную прокрутку ,

Макет также можно изменить с помощью CSSOM, например. манипулируя <style>.sheet.cssRules. CSS-анимации, уже упомянутые в комментариях, - это еще одна вещь, которая также может повлиять на макет без мутаций. И возможно анимация SMIL.

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

Если у вашего наложения есть некоторые эксплуатационные геометрические свойства, то другой возможностью может быть выборка частей видового экрана, которые важны для вас через document.elementFromPoint и вычисления ограничивающих прямоугольников найденных элементов и их детей до тех пор, пока у вас не будет того, что вам нужно. Планирование через requestAnimationFrame() означает, что вы должны иметь возможность отображать состояние текущего макета на каждом кадре, если оно не изменено другими обратными вызовами rAF, работающими после вашего.

В конце концов, большинство доступных методов, похоже, имеют некоторые пробелы или их нужно тщательно подстроить, чтобы не перегревать слишком много процессорного времени.

Или любой из этих подходов может быть достаточным, если его слегка изменить?

Сочетание отслеживаемых мутаций и WeakSet, чтобы не обрабатывать уже посещенные узлы, может работать с более тщательной фильтрацией.

  • имея уже посетили узел автоматически не означает, что вы можете пропустить его дети
  • но побывав ребенка без его целевая мутация сама по себе должна означать, что вы можете пропустить его
  • абсорбцию событий означает, что вы должны удалить все поддерево, узел на узле, из набора или просто четкого набора, так как они могут быть перемещены в другую точку в дереве
0

MutationRecords кажется listed in the order in which the changes happened (можно легко проверить).

Перед тем как запустить свой AnalyzeNode(nodes[i]) алгоритм, вы можете запустить этап AnalyzeChanges(mutations), который может определять все изменения, которые произошли.

Например, если вы видите addedNodes, он содержит один и тот же узел 10 раз, но вы видите один и тот же узел всего 9 раз в removedNodes, тогда вы знаете, что итоговым результатом является то, что узел был добавлен в DOM.

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

Тогда, наконец, когда вы знаете, что было чистым изменением, вы можете запустить AnalyzeNode(nodes[i]).

Я думаю об этом, чтобы наблюдать за всем деревом <svg> и визуализировать его (и повторно отображать его, когда происходят изменения) в WebGL.

Это может быть сложно, потому что представьте себе происходит следующее синхронно каким-то пользователь (вы не знаете, что он/она будет делать), которые манипулируют DOM:

  • добавляется поддерево (очереди, запись для корневого узла в addedNodes)
  • поддерево поддерева удаляется (очереди записи)
  • затем прилагаемые где-то за пределами первого поддерева (очереди другой записи, о мальчик.)
  • элементом является удаляется из другого поддерева (удаляется запись в очередь)
  • и добавил обратно в исходное поддерева (Очереди запись)
  • и т.д.
  • и т.д.

Наконец, вы получите список MutationRecords, который подробно все эти шаги.

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

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

Люди пытались решить проблему обнаружения изменений между деревьями.

Многие из этих решений связаны с терминами «virtual dom» и «dom diffing» до веб-сайта, которое yieldresults в Google.


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

Например, виртуальный дом имеет способ get a diff. Я еще не пробовал. Я также не уверен, как создать VTrees из деревьев DOM, чтобы передать их в функцию diff().

Aha! Может быть проще получить diff с diffDOM.

Получение diff может представлять собой простейший набор изменений, необходимый для перехода от одного дерева к другому дереву, что может быть намного проще, чем анализ списка записей мутаций. Это стоит попробовать. Я мог бы отправить обратно то, что я нахожу, когда я это делаю с помощью <svg> s ...

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