脚本修改body.innerHTML从身体 - 有时IE8中止,有时不?

时间:2011-12-27 19:39:49

标签: javascript xhtml innerhtml race-condition

由于我们的客户需要一个响应式(CSS)网站以及在其最基本的CMS平台中工作的要求,我们编写了一个脚本以配合页面的自定义HTML:

  1. 将我们的HTML保存到变量
  2. 清除整个headdocument
  3. 然后将我们的自定义HTML添加回document.body作为唯一的HTML内容。
  4. 这可确保CMS中没有任何垃圾HTML,<style><link>项目干扰响应式布局。我们的自定义CSS要求我们一直到body元素。

    测试页面有问题: https://www.eiseverywhere.com/ehome/index.php?eventid=31648&tabid=53961

    但是,脚本是在document的嵌套表中运行的,因为这是CMS放置内容的位置(在其预设页面模板中)。

    在所有现代浏览器中,包括FF,Chrome,Safari,iOS和IE9,这似乎都能顺利进行(尽管通常是“通过”)。

    使用IE8,每次重新加载页面时都会发生几种不同的事情:

    1. 页面加载没有错误,但只有HTML元素可见(黑色背景)。所有其他元素都是完全不可见的,但有(例如,如果找到,可以点击链接)。

    2. 如上所述,页面加载黑色和不可见,但状态栏指示错误927917

    3. 页面开始明显加载内容(背景图片等),但是被Operation Aborted 927917错误弹出中断,然后重定向到空白白色“无法加载页面”页面。

    4. 页面正常加载所有内容,没有任何错误。

    5. 在尝试加载页面时,IE8中发生了这四件事,我们不确定导致这种不一致的原因。这使得排除故障非常困难。

      使用IE7,我们得到了类似的不一致结果,以及已知错误927917

      根据要求,代码和内容,这里的首要问题是,执行此内容交换/注入的最佳方式是什么?我们只对支持IE7 +感兴趣,如果太麻烦,我们愿意放弃IE7。

      我们已经尝试了以下一些建议,但是在IE7和IE8之间得到了混合的结果,这些都没有完全解决上面列出的问题:

      http://www.timvasil.com/blog14/post/2008/05/28/Fix-for-IEs-Operation-Aborted-error.aspx

      http://blogs.msdn.com/b/ie/archive/2008/04/23/what-happened-to-operation-aborted.aspx

      在我们的脚本开始之前

      CMS输出:

      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <title>Title</title>
      
      <!-- CMS included meta tags -->
      <meta name="description" content="event title" />
      <meta name="keywords" content="meta-tags" />
      
      <!-- CMS included scripts -->
      <script language="javascript" type="text/javascript" src="/include/scripts/scripts.js"></script>
      <script language="javascript" type="text/javascript" src="/ehome/include/scripts.js"></script>
      
      <style type="text/css"> /* CMS styles */ </style>
      
      <!-- CMS JS Library inclusion - could possible use this to wait for DOM ready -->
      <script type="text/javascript" src="/include/scripts/prototype.js">
      
      </script>
      </head>
      
      <body>
      <form>
      <input type="hidden" id="module" value="ehome" /><input type="hidden" id="eventid" value="31648" />
      </form>
      
      <table id="shadow_table">
      <tr>
      <td></td>
      
      <td>
      <table id="outer_table">
      
      <!-- Lots of CMS table HTML -->
      
      <tr>
      <td id="inner_content">
      <div class="eh_outer_div">
      <div id="main_section">
      <div>
      <div>
      
          <!-- BEGIN OUR CUSTOM CODE - intended to replace body contents -->
      
          <div id="wrap">
              <!--[if (gte IE 6)&(lte IE 8)]>
                  <script src="https://www.eiseverywhere.com/docs/562/selectivizr.js"></script>
              <![endif]-->
              <!--[if lt IE 9]>
                  <script src="https://www.eiseverywhere.com/docs/562/html5.js"></script>
                  <script src="https://www.eiseverywhere.com/docs/562/iemq.js"></script>
              <![endif]-->
      
              <header>
                  <!-- header content... -->
              </header>
      
              <div id="main">
      
                  <ul>
                      <li data-id="home"><a href="https://www.eiseverywhere.com/ehome/index.php?eventid=31648&tabid=50283">Home</a></li>
                      <!-- Other nav items... --> 
                  </ul>
      
                  <h1>speakers</h1>
      
                  <section id="content">
                      <!-- Begin Main Content Section -->
      
                      <article>
                          <h2>CITE <strong>2012 speakers</strong></h2>
      
                          <p><strong>Our speaker list is growing continuously, so check back often for updates. Our esteemed roster so far includes:</strong></p>
      
                          <!-- Misc main content - etc.. -->
      
                      </article>
                  </section>
      
              </div><!-- End Main -->
      
          </div><!-- End #wrap Wrapper -->
      
          <footer>
              <div>
                  <ul>
                      <li data-id="home"><a href="https://www.eiseverywhere.com/ehome/index.php?eventid=31648&tabid=50283">Home</a></li>
                      <!-- Other nav items... --> 
                  </ul>
      
                  <!-- Misc footer content... -->
              </div>
          </footer> <!-- End Footer -->
      
          <!-- Cleaning Code -->
          <!-- THIS IS WHERE THE REPLACEMENT SCRIPTING IS (<SCRIPT>) -->
      
          <!-- END CUSTOM CONTENT -->
      
      </div>
      </div>
      </div>
      </div>
      </td>
      </tr>
      
      <!-- LOTS OF OTHER UNNECESSARY CMS TABLE MARKUP... -->
      
      </table>
      
      <!-- Garbage DIVs from CMS scripts -->
      <div id="overlay"></div>
      <div id="home_od"></div>
      
      </body>
      </html>
      

      更新:我们使用DOM ready()的自定义替换/注入脚本:

      <script type="text/javascript">
      /* Clean.js /// Switches out host HTML and replaces with clean custom HTML */
      
      document.observe('dom:loaded', function() { // Prototype.js listener: http://www.prototypejs.org/api/document/observe
      
      
          /* PAGE VARIABLES */
      
          var pre = '<!--[if lt IE 7 ]><div id="wrap" class="ie pre9 ie6"><![endif]--><!--[if IE 7 ]><div id="wrap" class="ie pre9 ie7"><![endif]--><!--[if IE 8 ]><div id="wrap" class="ie pre9 ie8"> <![endif]--><!--[if IE 9 ]><div id="wrap" class="ie ie9"> <![endif]--><!--[if (gt IE 9)|!(IE)]><!--><div id="wrap"><!--<![endif]-->';
      
              var wrap = document.getElementById('wrap');
              var title = wrap.getElementsByTagName('h1')[0].innerHTML;
              var pgid = title.replace(/[^a-zA-Z 0-9]+/g,'-').toLowerCase().replace(/[ ]/g, '-'); // clean H1 text and use as page id
              var footer = "<footer>" + document.getElementsByTagName('footer')[0].innerHTML + "</footer>";
              var content = pre + wrap.innerHTML + footer;
      
          var post = '</div>';
      
      
          /* HOUSE CLEANING */
      
          // Clean out <HEAD>
          emptyHead(); //remove all style blocks from head
          removeLinks("js"); //remove all occurences of "somescript.js" on page
          removeLinks("css"); //remove all occurences "somestyle.css" on page
      
          // Stage <BODY> 
          document.body.setAttribute("id", pgid);
          document.body.setAttribute("onload", "finished('"+pgid+"')");
          document.title = "CITE Expo + Conference: March 4-6, 2012 | " + title;
          document.body.innerHTML = content + post;
      
      //  document.body.insertAdjacentElement('afterBegin', document.getElementsByTagName('footer')[0]);
      //  document.body.insertAdjacentElement('afterBegin', wrap);
      //  document.body.removeChild(document.getElementById('shadow_table'));
      
          // Add meta tags
          addMetatag('viewport', 'head');
      
          // Add stylesheets
          addStylesheet('https://www.eiseverywhere.com/file_uploads/d1d7aeedb561585d601535bb14aa6910_cite.css', 'head');
          addStylesheet('https://www.eiseverywhere.com/file_uploads/cead2f725f0acc8b4ada117d22b2e872_type.css', 'head');
          addStylesheet('https://www.eiseverywhere.com/file_uploads/41062e7af46018760d01e8d9f7a7b9a9_flex.css', 'head');
          addStylesheet('https://www.eiseverywhere.com/file_uploads/fff3d12e98686a02eb8e8f2ac45f7bf8_anm8.css', 'head');
          addStylesheet('https://www.eiseverywhere.com/file_uploads/64388fdb8762f9232b2330f9e196f996_benton-gothic-cite.css', 'head');
          addStylesheet('https://www.eiseverywhere.com/file_uploads/f92ea468d4be1013b77010d2c42fdc6f_stag-cite.css', 'head');
      
          // Add javascript
      /*  addJavascript('https://www.eiseverywhere.com/file_uploads/3df3d4e3b7e57d447396fe2608c96664_jump.js', 'body'); // Not working well - need to inject a new element */
          addJavascript('https://www.eiseverywhere.com/file_uploads/b8601cc668024431aae05a886084fc37_android21.js', 'body');
      
      
          /* FUNCTIONS */
      
          function emptyHead()
          {
              var head = document.getElementsByTagName('head')[0];
              var s = head.getElementsByTagName('style');
              if(s)
                  while(s.length >= 1)
                      head.removeChild(s[0]);
          }
      
          function emptyBody()
          {
          //  document.body.removeChild(document.getElementById("overlay"));
          //  document.body.removeChild(document.getElementById("home_od"));
          }
      
          function addJavascript(jsname, pos)
          {
              var th = document.getElementsByTagName(pos)[0];
              var s = document.createElement('script');
              s.setAttribute('type', 'text/javascript');
              s.setAttribute('src', jsname);
              th.appendChild(s);
          }
      
          function addStylesheet(cssname, pos2)
          {
              var th2 = document.getElementsByTagName(pos2)[0];
              var s2 = document.createElement('link');
              s2.setAttribute('type', 'text/css');
              s2.setAttribute('href', cssname);
              s2.setAttribute('media', 'screen');
              s2.setAttribute('rel', 'stylesheet');
              th2.appendChild(s2);
          }
      
          function addMetatag(meta, pos3)
          {
              var th3 = document.getElementsByTagName(pos3)[0];
              var s3 = document.createElement('meta');
              s3.setAttribute('name', meta);
              s3.setAttribute('content', 'width=device-width, minimum-scale=1.0, maximum-scale=1.0');
              th3.appendChild(s3);
          }
      
          function removeLinks(filetype)
          {
              var targetelement = (filetype == "js") ? "script" : (filetype == "css") ? "link" : "none" //determine element type to create nodelist from
              var targetattr = (filetype == "js") ? "src" : (filetype == "css") ? "href" : "none" //determine corresponding attribute to test for
              var allsuspects = document.getElementsByTagName(targetelement)
              for (var i = allsuspects.length; i >= 0; i--) //search backwards within nodelist for matching elements to remove
                  if (allsuspects[i] && allsuspects[i].getAttribute(targetattr) != null)
                      allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
          }
      
          document.stopObserving("dom:loaded")
      
      });
      
      function finished(pid) // DOM is [natively] ready
      {
          setMenu(pid); // set the active class on li elements that contain a link to the current page
      //  emptyBody(); // remove junk divs from bottom of page
          setLayout(); // if <aside> is missing, set #main to .single-column class
      
          if(pid == "home") {
              var anm8 = '<object type="application/x-shockwave-flash" data="http://client.clevelanddesign.com/CD/IDG/CITE/content/intro.swf"><!-- By default, play flash video --><param name="movie" value="http://client.clevelanddesign.com/CD/IDG/CITE/content/intro.swf" /><param name="wmode" value="transparent" /><param name="scale" value="default" /><!-- Show iPad/iPhone/iPod visitors the HTML5 video --><video autoplay poster="https://www.eiseverywhere.com/file_uploads/e82b83f31bb069e818fe691ebc824cf1_mtng-locked.png"><source src="https://www.eiseverywhere.com/docs/562/intro-compressed.mp4" type="video/mp4"></source></video><!-- Backup image if all else fails --><img src="https://www.eiseverywhere.com/file_uploads/e82b83f31bb069e818fe691ebc824cf1_mtng-locked.png" alt="Meet the next generation of business" /></object>';
              document.getElementById('hero').innerHTML = anm8; // if home page, add animation after page is loaded
          }
          window.scrollTo(0, 1); // scroll mobile address bars out of sight (iPhone, Android) - gives us more screen space
      }
      
      function setMenu(p) // compare cleaned H1 text to link IDs // requires consistent naming between H1 and li.data-id // update topnav + footer HTML snippets in eTouches to match
      {
          var mitems = document.getElementsByTagName('li');
          for(i = 0; i < mitems.length; i++) {
              if(mitems[i].getAttribute('data-id'))
                  if(mitems[i].getAttribute('data-id') == p)
                      mitems[i].setAttribute("class", "active");
          }
      }
      
      function setLayout()
      {
          if(!document.getElementsByTagName('aside').length)
              document.getElementById("main").setAttribute("class", "single-column");
      }
      
      </script>
      
      <!--[if IE 7 ]>
      <script type="text/javascript">
      //  alert('Please upgrade to a modern browser to enjoy this website properly.');
      </script>
      <![endif]-->
      

2 个答案:

答案 0 :(得分:1)

在所有浏览器(特别是IE)中,您不能/不应该修改仍在加载过程中的元素。因此,您无法从<body>元素中的代码修改<body>元素的内容。 IE,特别是可以/将崩溃。不仅是页面级崩溃,而且有时整个浏览器崩溃。

对此的典型解决方案是确保修改页面的所有脚本必须等到修改页面安全。像YUI和jQuery这样的典型Web框架具有内置方法,用于等待整个页面被安全加载(因此可以进行操作)或者甚至可以安全地修改部分DOM。如果您没有使用任何内置此功能的Web框架,那么您可以找到带有搜索的代码片段,这样您就可以构建自己的小功能。逻辑因浏览器和浏览器版本而异。

绝对最简单的安全DOM修改版本是从位于<body>末尾的javascript函数调用启动DOM修改。如果您没有可用的代码框架,您可以添加自己的跨浏览器DOMReady函数,如this reference中所示,向下滚动到“Crossbrowser DOMContentLoaded处理代码”。

答案 1 :(得分:0)

删除“defer”属性并将操作代码封装在文档就绪事件(a la Prototype.js)中都解决了原始问题中描述的问题。 IE 7/8不再无法加载或加载不一致。

感谢@ jfriend00和@Alfabravo的提示。

我仍然遇到(所有浏览器)本机onLoad主体事件及其功能未触发的问题,以及IE 7/8随机丢弃样式表的事实,但我认为这是一个单独的问题。