Подтвердить что ты не робот

Восстановить точный innerHTML до DOM

Я хочу сохранить html-строку DOM, а затем восстановить ее точно так же. Код выглядит примерно так:

var stringified = document.documentElement.innerHTML
// later, after serializing and deserializing
document.documentElement.innerHTML = stringified

Это работает, когда все идеально, но когда DOM не является w3c-comliant, возникает проблема. Первая строка отлично работает, stringified точно соответствует DOM. Но когда я восстанавливаю из (не-w3c-совместимый) stringified, браузер делает некоторую магию, а итоговая DOM не такая, какая была изначально.

Например, если мой оригинальный DOM выглядит как

<p><div></div></p>

то окончательный DOM будет выглядеть как

<p></p><div></div><p></p>

поскольку элементы div не могут находиться внутри элементов p. Есть ли способ заставить браузер использовать тот же синтаксический анализ html, который он делает при загрузке страницы, и принять сломанный html as-is?

Почему html сломан в первую очередь? DOM не контролируется мной.

Здесь jsfiddle, чтобы показать поведение http://jsfiddle.net/b2x7rnfm/5/. Откройте консоль.

<body>
    <div id="asdf"><p id="outer"></p></div>
    <script type="text/javascript">
        var insert = document.createElement('div');
        var text = document.createTextNode('ladygaga');
        insert.appendChild(text);
        document.getElementById('outer').appendChild(insert);
        var e = document.getElementById('asdf')
        console.log(e.innerHTML);
        e.innerHTML = e.innerHTML;
        console.log(e.innerHTML); // This is different than 2 lines above!!
    </script>
</body>
4b9b3361

Ответ 1

Если вам нужно сохранить и восстановить недопустимую структуру HTML, вы можете сделать это с помощью XML. Следующий код следует из этой скрипты.

Чтобы сохранить, вы создаете новый документ XML, к которому вы добавляете узлы, которые вы хотите сериализовать:

var asdf = document.getElementById("asdf");
var outer = document.getElementById("outer");
var add = document.getElementById("add");
var save = document.getElementById("save");
var restore = document.getElementById("restore");

var saved = undefined;
save.addEventListener("click", function () {
  if (saved !== undefined)
    return; /// Do not overwrite

  // Create a fake document with a single top-level element, as 
  // required by XML.    
  var parser = new DOMParser();
  var doc = parser.parseFromString("<top/>", "text/xml");

  // We could skip the cloning and just move the nodes to the XML
  // document. This would have the effect of saving and removing 
  // at the same time but I wanted to show what saving while 
  // preserving the data would look like    
  var clone = asdf.cloneNode(true);
  var top = doc.firstChild;
  var child = asdf.firstChild;
  while (child) {
    top.appendChild(child);
    child = asdf.firstChild;
  }
  saved = top.innerHTML;
  console.log("saved as: ", saved);

  // Perform the removal here.
  asdf.innerHTML = "";
});

Чтобы восстановить, вы создаете документ XML для десериализации сохраненного вами и добавления узлов в документ:

restore.addEventListener("click", function () {
  if (saved === undefined)
      return; // Don't restore undefined data!

  // We parse the XML we saved.
  var parser = new DOMParser();
  var doc = parser.parseFromString("<top>" + saved + "</top>", "text/xml");
  var top = doc.firstChild;

  var child = top.firstChild;
  while (child) {
    asdf.appendChild(child);
    // Remove the extra junk added by the XML parser.
    child.removeAttribute("xmlns");
    child = top.firstChild;
  }
  saved = undefined;
  console.log("inner html after restore", asdf.innerHTML);
});

С помощью скрипта вы можете:

  • Нажмите кнопку "Добавить LadyGaga...", чтобы создать недопустимый HTML.

  • Нажмите "Сохранить и удалить из документа", чтобы сохранить структуру в asdf и очистить ее содержимое. Это печатает на консоли то, что было сохранено.

  • Нажмите "Восстановить", чтобы восстановить сохраненную структуру.

Приведенный выше код должен быть общим. Было бы возможно упростить код, если некоторые предположения могут быть сделаны относительно сохраняемой структуры HTML. Например, blah не является хорошо сформированным XML-документом, потому что вам нужен один верхний элемент в XML. Поэтому приведенный выше код требует усилий для добавления элемента верхнего уровня (top) для предотвращения этой проблемы. Также, как правило, невозможно просто проанализировать сериализацию HTML как XML, чтобы операция сохранения сериализовалась в XML.

Это доказательство концепции больше всего. Могут быть побочные эффекты от перемещения узлов, созданных в документе HTML, в документ XML или наоборот, который я не ожидал. Я запустил код выше в Chrome и FF. У меня нет IE под рукой, чтобы запустить его там.

Ответ 2

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


Так как использование DOM, по-видимому, позволяет вам в некоторой степени сохранить недопустимую структуру и использовать innerHTML включает в себя повторную обработку (как вы заметили) побочных эффектов, мы должны смотреть не на использование innerHTML:

Вы можете клонировать оригинал, а затем менять его в клоне:

var e = document.getElementById('asdf')
snippet.log("1: " + e.innerHTML);
var clone = e.cloneNode(true);
var insert = document.createElement('div');
var text = document.createTextNode('ladygaga');
insert.appendChild(text);
document.getElementById('outer').appendChild(insert);
snippet.log("2: " + e.innerHTML);
e.parentNode.replaceChild(clone, e);
e = clone;
snippet.log("3: " + e.innerHTML);

Живой пример:

var e = document.getElementById('asdf')
snippet.log("1: " + e.innerHTML);
var clone = e.cloneNode(true);
var insert = document.createElement('div');
var text = document.createTextNode('ladygaga');
insert.appendChild(text);
document.getElementById('outer').appendChild(insert);
snippet.log("2: " + e.innerHTML);
e.parentNode.replaceChild(clone, e);
e = clone;
snippet.log("3: " + e.innerHTML);
<div id="asdf">
  <p id="outer">
    <div>ladygaga</div>
  </p>
</div>

<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Ответ 3

Попробуйте использовать Blob, URL.createObjectURL для экспорта html; включить тег script в экспортированный html, который удаляет элементы <div></div><p></p> из отображаемого документа html

HTML

<body>
    <div id="asdf">
        <p id="outer"></p>
    </div>
    <script>
        var insert = document.createElement("div");
        var text = document.createTextNode("ladygaga");
        insert.appendChild(text);
        document.getElementById("outer").appendChild(insert);
        var elem = document.getElementById("asdf");
        var r = document.querySelectorAll("[id=outer] ~ *");
        // remove last `div` , `p` elements from `#asdf`
        for (var i = 0; i < r.length; ++i) {
            elem.removeChild(r[i])
        }
    </script>
</body>

JS

var e = document.getElementById("asdf");   
var html = e.outerHTML;  
console.log(document.body.outerHTML);   
var blob = new Blob([document.body.outerHTML], {
    type: "text/html"
});   
var objUrl = window.URL.createObjectURL(blob);
var popup = window.open(objUrl, "popup", "width=300, height=200");

jsfiddle http://jsfiddle.net/b2x7rnfm/11/

Ответ 4

см. этот пример: http://jsfiddle.net/kevalbhatt18/1Lcgaprc/

MDN cloneNode

<я >

var e = document.getElementById('asdf')
console.log(e.innerHTML);
backupElem = e.cloneNode(true);
// Your tinkering with the original
e.parentNode.replaceChild(backupElem, e);
console.log(e.innerHTML);

Ответ 5

Вы не можете ожидать, что HTML будет анализироваться как несоответствующий HTML. Но так как структура скомпилированного несоответствующего HTML очень предсказуема, вы можете сделать функцию, которая делает HTML несовместимым снова следующим образом:

function ruinTheHtml() {

var allElements = document.body.getElementsByTagName( "*" ),
    next,
    afterNext;

Array.prototype.map.call( allElements,function( el,i ){

    if( el.tagName !== 'SCRIPT' && el.tagName !== 'STYLE' ) {

        if(el.textContent === '') {

            next = el.nextSibling;

            afterNext = next.nextSibling;

            if( afterNext.textContent === '' ) {

                el.parentNode.removeChild( afterNext );
                el.appendChild( next );

            }

        }

    }
});

}

Смотрите скрипку: http://jsfiddle.net/pqah8e25/3/

Ответ 6

Вы должны клонировать node вместо копирования html. Правила синтаксического анализа заставят браузер закрыть p при просмотре div.

Если вам действительно нужно получить html из этой строки, и это действительно xml, вы можете использовать следующий код ($ is jQuery):

var html = "<p><div></div></p>";
var div = document.createElement("div");
var xml = $.parseXML(html);
div.appendChild(xml.documentElement);
div.innerHTML === html // true

Ответ 7

Вы можете использовать outerHTML, он сохраняет прежнюю структуру:

(на основе вашего исходного образца)

<div id="asdf"><p id="outer"></p></div>

<script type="text/javascript">
    var insert = document.createElement('div');
    var text = document.createTextNode('ladygaga');
    insert.appendChild(text);
    document.getElementById('outer').appendChild(insert);
    var e = document.getElementById('asdf')
    console.log(e.outerHTML);
    e.outerHTML = e.outerHTML;
    console.log(e.outerHTML);
</script>

Демо: http://jsfiddle.net/b2x7rnfm/7