What's wrong with my HTML that jQuery doesn't parse it?
I am trying to get an HTML file and assign it to a variable as a jQuery object. But to no avail. I'm not sure if Stack Snippets allow GET requests, so here's a 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)));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
HTML is valid. However, I am unable to get it in the object. The returned data is, as expected, a string. But when I try to parse this string as HTML using, $.parseHTML()
or put it in a jQuery selector, $()
or even try both, I always get the same result: an array containing the title and main element.
So somehow, the way jQuery still parses it and creates an array of element in the head and one element in the body. But why? And how can I fix this and turn it into a jQuery object? I'm only interested in the contents of the body.
There is a similar question but the accepted solution does not help as it is included in the JSFiddle and I am also experiencing this problem locally with XAMPP.
source to share
document.querySelector("body").innerHTML = +
'<object type="text/html" data="'+html+'" ></object>';
... just works. So I expected:
let parsedHtml = $('<object />', {
type:'text/html',
data:html
}).html()
also simple to work, but I'm guessing some magic happens when someone actually adds it to the DOM. It parses it, creates a CSSOM and DOM for it and loads all dependencies, basically what you would expect from a browser with a resource .html
.
So, here's how to do it:
- Create a dummy placeholder,
- put
<object>
inside, - add fiction to the DOM,
- get
.contents()
from<object>
(onload
event on it ), - and delete the dummy file.
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 display:none
from a parent and not download <object>
. This is why I used height:0;overflow:hidden;
. You will notice that the content of this element does not have the typical structure of a jQuery object, because it is a document. you will find juice in
dummyDiv.find('object').contents()[1].innerHTML
When the html string is already in a variable, it loads instantly, but in reality you need to place the listener onload
on <object>
and only run 4. and 5. when the listener starts.
source to share
Install the variant . Use to parse lines into ; to get an ad ; write and disassemble into current or other .jQuery.ajax()
processData
false
DOMParser()
html
document
XMLSerizlizer()
DOCTYPE
document.write()
DOCTYPE
document
.documentElement.outerHTML
html
document
processData strong> (default:
true
)Type: Boolean
By default, the data is passed to the parameter
data
as an object (technically, nothing but a string) will be processed and converted to a query string, matching the default content type "Application / x-WWW-forms-urlencoded". If you want to send a DOMDocument or other unprocessed data, set this parameter tofalse
.
I'm not sure if Stack Snippets allow GET requests
Yes. It is possible to request the request GET
by setting url
in data URI
or Blob URL
the resource view in $.ajax()
, XMLHttpRequest()
or fetch()
, see Do you have an "echo page" for inspecting 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>
Also can be imported html
document
into current document
using element <link>
with attribute rel
set to "import"
, see How to add whole html file with jquery .
source to share
I always get the same result: an array containing the title and main element.
What exactly is the problem?
jQuery still parses it and creates an array of element in the head and one element in the body. But why?
What are you expecting?
This is similar to 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 looks like it does what it should.
I'm only interested in the contents of the body.
The body content main
, which you noted is the second item.
What do you want to do about 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())
source to share