Bram Vanroy
Bram Vanroy

Reputation: 28564

What is wrong with my HTML that jQuery doesn't parse it?

I've been trying to GET a HTML file and assign it to a variable as a jQuery object. To no avail. I'm not sure if Stack Snippets allow GET requests, so here is a JSFiddle link as well.

    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)));
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

The HTML is valid. Yet, I can't get it in an object. The returned data is, as expected, a string. But when I try to parse that string as HTML using $.parseHTML() or putting it in a jQuery selector $() or even try both I always get the same result: an array containing the title and main element.

So some way some how jQuery still parses it and makes an array of the element in the head and the one element in the body. But why? And how can I remedy this, and transform it into a jQuery object? I am only interested in the contents of the body.

There is a similar question, but the accepted solution doesn't help as it goes into JSFiddle and I'm also experiencing this problem locally with XAMPP.

Upvotes: 3

Views: 863

Answers (4)

tao
tao

Reputation: 90287

document.querySelector("body").innerHTML = + 
  '<object type="text/html" data="'+html+'" ></object>';

...just works. So I'd have expected:

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

to also just work, But I'm guessing some magic happens when one actually adds it to DOM. It parses it, builds CSSOM and DOM for it and loads all dependencies, basically what you'd expect a browser to do with an .html resource.

So here's a way to do it:

  1. Create a dummy placeholder,
  2. place the <object> inside,
  3. append dummy to DOM,
  4. get .contents() of <object> (on it's onload event),
  5. and delete the dummy.
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());

Note Chrome is already smart enough to detect a display:none on a parent and not load the <object>. That's why I used height:0;overflow:hidden;. You will notice the contents of this element doesn't have a typical stucture of a jQuery object, because it's a document. you'll find the juice in

dummyDiv.find('object').contents()[1].innerHTML

When the html string is already in a variable, it loads instantly, but in reality you'll need to place an onload listener on the <object> and only run 4. and 5. when the listener triggers.

Upvotes: 2

ハセン
ハセン

Reputation: 387

I always get the same result: an array containing the title and main element.

What exactly is the problem?

jQuery still parses it and makes an array of the element in the head and the one element in the body. But why?

What are you expecting?

That seems to be the behavior of parseHtml.

Here's another example:

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

It returns an array with one element: div

If you look at the docs for parseHTML it says:

Parses a string into an array of DOM nodes.

So it seems to be doing exactly what it's supposed to.

I am only interested in the contents of the body.

The content of the body is main which as you have noted is the second element.

What do you want to do with it?

You can wrap it in a jQuery object by passing it to the jQuery constructor.

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())

Upvotes: 0

Asav Vora
Asav Vora

Reputation: 61

Problem is you are trying to access direct html but actually its a string.

So you have to render html to access data.

For that store response data into any html element and access through it.

I changed your jsfiddle so you can see.

Upvotes: 0

guest271314
guest271314

Reputation: 1

Set jQuery.ajax() processData option to false. Use DOMParser() to parse string to an html document; XMLSerizlizer() to get DOCTYPE declaration; document.write() to write DOCTYPE and parsed document .documentElement.outerHTML to current or other html document.

processData (default: true)

Type: Boolean

By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.


I'm not sure if Stack Snippets allow GET requests

Yes. It is possible to echo GET request by setting url to data URI or Blob URL representation of resource at $.ajax(), XMLHttpRequest() or fetch(), see 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);
  });
  
  
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

It is also possible to import an html document to current document using <link> element with rel attribute set to "import", see How to append a whole html file with jquery.

Upvotes: 1

Related Questions