【问题标题】:What is wrong with my HTML that jQuery doesn't parse it?我的 jQuery 不解析的 HTML 有什么问题?
【发布时间】:2017-10-09 10:54:48
【问题描述】:

我一直在尝试获取一个 HTML 文件并将其作为 jQuery 对象分配给一个变量。无济于事。我不确定 Stack Snippets 是否允许 GET 请求,所以这里也是 JSFiddle link

    var html = '<!DOCTYPE html><html lang="en"><head><title>Template</title></head><body itemscope itemtype="http://schema.org/WebPage"><main><header itemscope itemtype="http://schema.org/Country" itemprop="about"><h1 itemprop="name">Dummy heading</h1><p><span class="capital" title="Capital" itemprop="containsPlace"></span><span title="Dummy title" itemprop="additionalProperty" itemscope itemtype="http://schema.org/PropertyValue"><meta itemprop="name" content="Member of the EU since"><span itemprop="value" class="member-since">Dummy year</span></span></p></header><div itemprop="mainEntity" itemscope itemtype="http://schema.org/ItemList"><meta itemprop="description" content=""><article class="recipe loading" itemprop="itemListElement" itemscope itemtype="http://schema.org/Recipe"><meta itemprop="position" content=""><aside class="media"><div class="img-gallery" itemscope itemtype="http://schema.org/ImageGallery"></div><div itemscope itemtype="http://schema.org/VideoObject" class="youtube"><a itemprop="contentUrl" href="#" title=""><meta itemprop="name" content=""><meta itemprop="uploadDate" content=""><meta itemprop="description" content=""><img itemprop="thumbnailUrl" src="#" alt=""></a><div class="youtube-player"></div></div></aside><div class="text"><div class="wiki-text"><h1 itemprop="name">Dummy heading</h1><p itemprop="description"></p><p class="read-more">For more information about <span class="recipe-name"></span>, read the <a href="#" title="" itemprop="sameAs">Wiki</a>.</p></div><div class="rating" itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">Rated <span itemprop="ratingValue">3.5</span>/5 based on <span itemprop="reviewCount">11</span> customer reviews</div><div class="cooking"><h2>Bake it yourself!</h2><div><meta itemprop="cookTime" content=""><span>Bake time: <span class="bake-time"></span></span></div><div class="ingredients-wrapper"><h3>Ingredients <small>for <span itemprop="recipeYield"></span></small></h3><div class="ingredients"><h4>Dummy heading</h4><ul></ul></div></div><div class="how-to"><h3>Steps</h3><ol></ol></div></div></div></article></div></main></body></html>';
    
    $.ajax({
      type: 'post',
      url: "/echo/html/",
      dataType: "html",
      data: {
        html: html,
        delay: 1
      }
    }).done(function(data) {
      // string
    	console.log(data);
      // array
      console.log($(data));
      // array
      console.log($.parseHTML(data));
      // array
      console.log($($.parseHTML(data)));
    });
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

HTML 有效。但是,我无法将其放入对象中。正如预期的那样,返回的数据是一个字符串。但是,当我尝试使用 $.parseHTML() 将该字符串解析为 HTML 或将其放入 jQuery 选择器 $() 甚至尝试两者时,我总是得到相同的结果:包含标题和主要元素的数组。

因此,在某种程度上,jQuery 仍然会解析它,并在头部元素和正文中的一个元素组成一个数组。但是为什么?我该如何解决这个问题,并将其转换为 jQuery 对象?我只对正文的内容感兴趣。

a similar question,但接受的解决方案在进入 JSFiddle 时并没有帮助,而且我在本地使用 XAMPP 也遇到了这个问题。

【问题讨论】:

    标签: jquery ajax html-parsing


    【解决方案1】:
    document.querySelector("body").innerHTML = + 
      '<object type="text/html" data="'+html+'" ></object>';
    

    ...只是工作。所以我已经预料到了:

    let parsedHtml = $('<object />', {
      type:'text/html',
      data:html
    }).html()
    

    也可以正常工作,但我猜当有人真正将它添加到 DOM 时会发生一些神奇的事情。它解析它,为其构建 CSSOM 和 DOM 并加载所有依赖项,这基本上是您期望浏览器使用 .html 资源所做的事情。

    所以这是一种方法:

    1. 创建一个虚拟占位符,
    2. &lt;object&gt;放在里面,
    3. 将 dummy 附加到 DOM,
    4. 获得&lt;object&gt;.contents()(在它的onload 事件上),
    5. 并删除虚拟对象。
    let html = '// your valid html...',
        dummyDiv = $('<div />',{
          html: '<object type="text/html" data="'+html+'" ></object>',
          style:"height:0;overflow:hidden;"
        });
    
    $('html').append(dummyDiv);
    console.log(dummyDiv.find('object').contents());
    

    注意 Chrome 已经足够智能,可以检测到父级上的 display:none 并且不加载 &lt;object&gt;。这就是我使用height:0;overflow:hidden; 的原因。你会注意到这个元素的内容没有典型的 jQuery 对象结构,因为它是一个文档。你会在

    中找到果汁
    dummyDiv.find('object').contents()[1].innerHTML
    

    当 html 字符串已经在变量中时,它会立即加载,但实际上您需要在 &lt;object&gt; 上放置一个 onload 侦听器,并且只运行 4. 和 5. 当监听器触发时。

    【讨论】:

    • 嗯,有什么方法可以在不先将元素添加到 DOM 的情况下做到这一点?问题是我想将内容加载到内存中,对其进行一些修改(克隆、分离、整个交易),然后将其添加到 DOM。在内存中,因为这应该比修改 DOM 更快。
    • 我不知道。事情就是这样被正确解析的。否则,您可以在字符串上运行一些 jQuery 技巧,但它不是 DOM 元素,因此并非所有技巧都可用。例如,在 $(data) 上运行 .find() 并按类选择会起作用,而按标签选择则不会,因为它不是 DOM。这是一个字符串。使用上述方法,您不仅要等待 ajax 加载,还要等待生成的 DOM 正确加载(以防万一它有一些 DOM 操作脚本)。
    • 我明白了。聪明的。因此,最好的办法可能是将其附加到 html/body 中,然后在 VAR 中执行 .detach(),从而有效地将解析后的内容放回内存中,从而删除虚拟对象。操作 VAR,并将 VAR 添加回 DOM?
    • 是的。 jQuery 将像在普通 DOM 上一样工作,因为它是普通 DOM,即使与现有 DOM 分离。但是,我不知道如何在不将其放入 DOM 的情况下触发它的解析/加载。我确信这是可能的(毕竟 DOM 做到了,对吗?)我只是对内部流程知之甚少。坦率地说,只要我得到结果,我就不在乎了,哈哈。
    • 我会暂时搁置这个问题,补充一点小悬赏,也许一些 jQuery 大师可以启发我们。 :-)
    【解决方案2】:

    jQuery.ajax() processData 选项设置为false。使用DOMParser() 将字符串解析为html documentXMLSerizlizer() 获取DOCTYPE 声明; document.write() 写入DOCTYPE 并将document .documentElement.outerHTML 解析为当前或其他html document

    processData(默认:true

    类型:布尔值

    默认情况下,数据作为对象传入data选项 (从技术上讲,除字符串之外的任何内容)将被处理并 转换为查询字符串,适合默认的内容类型 “应用程序/x-www-form-urlencoded”。如果你想发送一个 DOMDocument 或其他未处理的数据,将此选项设置为false


    我不确定 Stack Snippets 是否允许 GET 请求

    是的。可以通过将url 设置为data URIBlob URL 表示$.ajax()XMLHttpRequest()fetch() 的资源来回显GET 请求,请参阅Does Stack Overflow have an “echo page” to test AJAX requests, inside a code snippet?

    var html = `<!DOCTYPE html>
    <html lang="en">
    
    <head>
      <title>Template</title>
    </head>
    
    <body itemscope itemtype="http://schema.org/WebPage">
      <main>
        <header itemscope itemtype="http://schema.org/Country" itemprop="about">
          <h1 itemprop="name">Dummy heading</h1>
          <p><span class="capital" title="Capital" itemprop="containsPlace"></span><span title="Dummy title" itemprop="additionalProperty" itemscope itemtype="http://schema.org/PropertyValue"><meta itemprop="name" content="Member of the EU since"><span itemprop="value" class="member-since">Dummy year</span></span>
          </p>
        </header>
        <div itemprop="mainEntity" itemscope itemtype="http://schema.org/ItemList">
          <meta itemprop="description" content="">
          <article class="recipe loading" itemprop="itemListElement" itemscope itemtype="http://schema.org/Recipe">
            <meta itemprop="position" content="">
            <aside class="media">
              <div class="img-gallery" itemscope itemtype="http://schema.org/ImageGallery"></div>
              <div itemscope itemtype="http://schema.org/VideoObject" class="youtube">
                <a itemprop="contentUrl" href="#" title="">
                  <meta itemprop="name" content="">
                  <meta itemprop="uploadDate" content="">
                  <meta itemprop="description" content=""><img itemprop="thumbnailUrl" src="#" alt=""></a>
                <div class="youtube-player"></div>
              </div>
            </aside>
            <div class="text">
              <div class="wiki-text">
                <h1 itemprop="name">Dummy heading</h1>
                <p itemprop="description"></p>
                <p class="read-more">For more information about <span class="recipe-name"></span>, read the <a href="#" title="" itemprop="sameAs">Wiki</a>.</p>
              </div>
              <div class="rating" itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">Rated <span itemprop="ratingValue">3.5</span>/5 based on <span itemprop="reviewCount">11</span> customer reviews</div>
              <div class="cooking">
                <h2>Bake it yourself!</h2>
                <div>
                  <meta itemprop="cookTime" content=""><span>Bake time: <span class="bake-time"></span></span>
                </div>
                <div class="ingredients-wrapper">
                  <h3>Ingredients <small>for <span itemprop="recipeYield"></span></small></h3>
                  <div class="ingredients">
                    <h4>Dummy heading</h4>
                    <ul></ul>
                  </div>
                </div>
                <div class="how-to">
                  <h3>Steps</h3>
                  <ol></ol>
                </div>
              </div>
            </div>
          </article>
        </div>
      </main>
    </body>
    </html>`;
    
    
    $.ajax({
        type: "GET",
        url: "data:text/html," + html,
        processData: false
      })
      .then(function(data) {
        // string
        console.log(data);
        var parser = new DOMParser();
        // document
        var d = parser.parseFromString(data, "text/html");
        // `document`, `document` as jQuery object
        console.log(d, $(d));
        // get elements having `itemscope` attribute
        console.log($(d).find("[itemscope]"));
        // do stuff
        var dt = new XMLSerializer().serializeToString(d.doctype);
        document.write(dt, d.documentElement.outerHTML);
      })
      .fail(function(jqxhr, textStatus, errorThrown) {
        console.log(errorThrown);
      });
      
      
    &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

    还可以使用&lt;link&gt; 元素将html document 导入到当前document,并将rel 属性设置为"import",参见How to append a whole html file with jquery

    【讨论】:

    • 您也可以使用 XMLHttpRequest() 并将 .responseType 设置为 "document" 以在 loadreadystatechange 事件中将 #document 获取为 XMLHttpRequest.response,或 Response.blob(), @98764 @ 得到htmlBlob
    • 使用 dataType: html 有什么区别?这不就是它应该做的吗?将检索到的内容处理为html?
    • 使用dataType:"html" 返回相同的结果。
    • @BramVanroy 也就是同样使用DOMParser时应该返回相同的结果。
    【解决方案3】:

    问题是您尝试直接访问 html,但实际上它是一个字符串。

    所以你必须渲染 html 才能访问数据。

    用于将响应数据存储到任何 html 元素中并通过它进行访问。

    我更改了您的jsfiddle,以便您查看。

    【讨论】:

      【解决方案4】:

      我总是得到相同的结果:一个包含标题和主要元素的数组。

      到底是什么问题?

      jQuery 仍然会解析它并创建一个包含头部元素和正文中的一个元素的数组。但为什么呢?

      你期待什么?

      这似乎是 parseHtml 的行为。

      这是另一个例子:

      $.parseHTML("<div>Hello<span>World</span></div>")
      

      它返回一个包含一个元素的数组:div

      如果您查看docs for parseHTML,它会说:

      将字符串解析为 DOM 节点数组。

      所以它似乎正在做它应该做的事情。

      我只对正文的内容感兴趣。

      正文的内容是main,正如您所指出的,它是第二个元素。

      你想用它做什么?

      您可以通过将它传递给 jQuery 构造函数来将其包装在一个 jQuery 对象中。

      var html = '<!DOCTYPE html><html lang="en"><head><title>Template</title></head><body itemscope itemtype="http://schema.org/WebPage"><main><header itemscope itemtype="http://schema.org/Country" itemprop="about"><h1 itemprop="name">Dummy heading</h1><p><span class="capital" title="Capital" itemprop="containsPlace"></span><span title="Dummy title" itemprop="additionalProperty" itemscope itemtype="http://schema.org/PropertyValue"><meta itemprop="name" content="Member of the EU since"><span itemprop="value" class="member-since">Dummy year</span></span></p></header><div itemprop="mainEntity" itemscope itemtype="http://schema.org/ItemList"><meta itemprop="description" content=""><article class="recipe loading" itemprop="itemListElement" itemscope itemtype="http://schema.org/Recipe"><meta itemprop="position" content=""><aside class="media"><div class="img-gallery" itemscope itemtype="http://schema.org/ImageGallery"></div><div itemscope itemtype="http://schema.org/VideoObject" class="youtube"><a itemprop="contentUrl" href="#" title=""><meta itemprop="name" content=""><meta itemprop="uploadDate" content=""><meta itemprop="description" content=""><img itemprop="thumbnailUrl" src="#" alt=""></a><div class="youtube-player"></div></div></aside><div class="text"><div class="wiki-text"><h1 itemprop="name">Dummy heading</h1><p itemprop="description"></p><p class="read-more">For more information about <span class="recipe-name"></span>, read the <a href="#" title="" itemprop="sameAs">Wiki</a>.</p></div><div class="rating" itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">Rated <span itemprop="ratingValue">3.5</span>/5 based on <span itemprop="reviewCount">11</span> customer reviews</div><div class="cooking"><h2>Bake it yourself!</h2><div><meta itemprop="cookTime" content=""><span>Bake time: <span class="bake-time"></span></span></div><div class="ingredients-wrapper"><h3>Ingredients <small>for <span itemprop="recipeYield"></span></small></h3><div class="ingredients"><h4>Dummy heading</h4><ul></ul></div></div><div class="how-to"><h3>Steps</h3><ol></ol></div></div></div></article></div></main></body></html>';
      
      var parsed = $.parseHTML(html)
      var main = parsed[1]
      var $main = $(main)
      // $main is a jQuery object
      console.log("h4 content:", $main.find('h4').text())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-25
        • 2018-05-30
        • 2012-09-15
        • 1970-01-01
        • 1970-01-01
        • 2011-12-07
        相关资源
        最近更新 更多