Javascript 将精确的innerHTML恢复到DOM

Javascript 将精确的innerHTML恢复到DOM,javascript,jquery,html,firefox,dom,Javascript,Jquery,Html,Firefox,Dom,我想保存DOM的html字符串,然后将其还原为完全相同的字符串。代码如下所示: var stringified = document.documentElement.innerHTML // later, after serializing and deserializing document.documentElement.innerHTML = stringified function ruinTheHtml() { var allElements = document.body.get

我想保存DOM的html字符串,然后将其还原为完全相同的字符串。代码如下所示:

var stringified = document.documentElement.innerHTML
// later, after serializing and deserializing
document.documentElement.innerHTML = stringified
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 );

            }

        }

    }
});

}
当一切都很完美时,这是可行的,但是当DOM不是w3c兼容时,就会出现问题。第一行工作正常,
stringized
与DOM完全匹配。但是,当我从(不符合w3c的)
字符串化的
还原时,浏览器发挥了一些神奇的作用,生成的DOM与最初的不一样

例如,如果我的原始DOM看起来像

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

这不适用于您最近的澄清,即您必须拥有字符串副本。不过,把它留给其他可能更灵活的人


由于使用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'))
log(“1:+e.innerHTML”);
var clone=e.cloneNode(真);
var insert=document.createElement('div');
var text=document.createTextNode('ladygaga');
插入.appendChild(文本);
document.getElementById('outer').appendChild(insert);
log(“2:+e.innerHTML”);
e、 parentNode.replaceChild(克隆,e);
e=克隆;
log(“3:+e.innerHTML”)

高清


尝试利用
Blob
URL.createObjectURL
导出
html
;在导出的
html
中包含
script
标记,该标记从呈现的
html
文档中删除

元素

html

jshiddle参见以下示例:


您不能期望HTML被解析为不兼容的HTML。但是,由于编译后的不兼容HTML的结构是非常可预测的,您可以创建一个函数,使HTML再次不兼容,如下所示:

var stringified = document.documentElement.innerHTML
// later, after serializing and deserializing
document.documentElement.innerHTML = stringified
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 );

            }

        }

    }
});

}
看小提琴:
您必须克隆节点,而不是复制html。当看到
div
时,解析规则将强制浏览器关闭
p

如果您确实需要从该字符串获取html,并且它是有效的xml,那么您可以使用以下代码(
$
is
jQuery
):

var html=“

”; var div=document.createElement(“div”); var xml=$.parseXML(html); div.appendChild(xml.documentElement); div.innerHTML===html//true
您可以使用
outerHTML
,它保留了原始结构:

(基于您的原始样本)

var insert=document.createElement('div'); var text=document.createTextNode('ladygaga'); 插入.appendChild(文本); document.getElementById('outer').appendChild(insert); var e=document.getElementById('asdf') console.log(e.outerHTML); e、 outerHTML=e.outerHTML; console.log(e.outerHTML);

演示:

如果需要保存和恢复无效的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 = "";
});
var asdf=document.getElementById(“asdf”);
var outer=document.getElementById(“outer”);
var add=document.getElementById(“add”);
var save=document.getElementById(“保存”);
var restore=document.getElementById(“restore”);
var保存=未定义;
save.addEventListener(“单击”),函数(){
如果(已保存!==未定义)
return;///不覆盖
//使用单个顶级元素创建假文档,如下所示
//XML所要求的。
var parser=新的DOMParser();
var doc=parser.parseFromString(“,“text/xml”);
//我们可以跳过克隆,只需将节点移动到XML
//文档。这将具有保存和删除的效果
//同时,我想展示一下,在
//保存数据看起来像
var clone=asdf.cloneNode(true);
var top=doc.firstChild;
var child=asdf.firstChild;
while(儿童){
顶部。附加子对象(子对象);
child=asdf.firstChild;
}
saved=top.innerHTML;
console.log(“另存为:”,已保存);
//在此处执行删除操作。
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);
});
restore.addEventListener(“单击”),函数(){
如果(已保存===未定义)
return;//不还原未定义的数据!
//我们解析保存的XML。
var parser=新的DOMParser();
var doc=parser.parseFromString(“+saved+”,“text/xml”);
var top=doc.firstChild;
var child=top.firstChild;
while(儿童){
asdf.儿童(儿童);
//删除XML解析器添加的额外垃圾。
儿童移除属性(“xmlns”);
child=top.firstChild;
}
保存=未定义;
log(“还原后的内部html”,asdf.innerHTML);
});
使用小提琴,您可以:

  • 按“添加LadyGaga…”按钮创建无效的HTML

  • 按“保存并从文档中删除”将结构保存在
    asdf
    中并清除其内容。这会将保存的内容打印到控制台

  • 按“还原”以还原已保存的结构

  • 上面的代码是通用的。如果可以对要保存的HTML结构进行一些假设,则可以简化代码。例如,
    blah
    不是一个格式良好的XML文档,因为您需要XML中的一个顶层元素。因此,上面的代码煞费苦心地添加了一个顶级元素(
    top
    )来防止这个问题。通常也不可能仅将HTML序列化解析为XML,以便保存操作序列化为XML

    这比任何东西都更能证明概念。可能有希德
    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 );
    
                }
    
            }
    
        }
    });
    
    }
    
    var html = "<p><div></div></p>";
    var div = document.createElement("div");
    var xml = $.parseXML(html);
    div.appendChild(xml.documentElement);
    div.innerHTML === html // true
    
    <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>
    
    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 = "";
    });
    
    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);
    });