Bug Tracker

Modify

Ticket #7784 (closed bug: duplicate)

Opened 3 years ago

Last modified 3 years ago

$(str) is failing when str is an entire html document

Reported by: cowboy Owned by:
Priority: undecided Milestone: 1.6
Component: unfiled Version: 1.4.4
Keywords: Cc:
Blocking: Blocked by:

Description

Go to jquery.com, and enter this into the console. You'll see that while the html string is being logged, $(html) is not acting as expected, stripping the html, head and body tags and flattening the structure somewhat. As a result, $(html).find('body') returns nothing!

$.get( '/', function( html ){
  console.log( html ); // full html string, as-expected
  console.log( $(html) ); // html, head, body removed?
  console.log( $(html).find('body') ); // nothing ?!
});

Another example for the jquery.com console:

$('<div/>').load( '/', function(){
  console.log( this );
});

logs this:

<div>​
  <meta http-equiv=​"content-type" content=​"text/​html;​ charset=utf-8">​
  <title>​jQuery: The Write Less, Do More, JavaScript Library​</title>​
  <link rel=​"stylesheet" href=​"http:​/​/​static.jquery.com/​files/​rocker/​css/​reset.css" type=​"text/​css">​
  <link rel=​"stylesheet" href=​"http:​/​/​static.jquery.com/​files/​rocker/​css/​screen.css" type=​"text/​css">​
  <link rel=​"alternate" type=​"application/​rss+xml" title=​"jQuery Blog" href=​"http:​/​/​jquery.com/​blog/​feed/​">​
  <link rel=​"shortcut icon" href=​"http:​/​/​static.jquery.com/​favicon.ico" type=​"image/​x-icon">​
  <div id=​"jq-siteContain">​…​</div>​
  <!-- /#siteContain -->
</div>​

And this:

$('<div/>').load( '/ body', function(){
  console.log( $(this).html() );
});

logs nothing!

Change History

comment:1 Changed 3 years ago by cowboy

FWIW, I have been operating under the impression that the whole point of $.fn.load was to be able to load (and select from) entire html documents, as per the examples at  http://api.jquery.com/load/.

Placing this seemingly arbitrary restriction that a developer can't target the html, head, or body elements of deserialized entire-html-document strings in $(str) or $.fn.load isn't great.

Of course, I'm sure it's not actually arbitrary, it only seems arbitrary to me as I try to do the following:

A common use case for $.fn.load might be to very quickly "ajaxify" a site by "hijacking" internal links with a delegated click handler, loading the target page's html via GET request and then replacing the existing body's contents with the newly loaded page's body contents.

So perhaps this use-case isn't as common as I thought, because it doesn't work!

Maybe this is an issue of documentation. Maybe it can be fixed without too much effort. Either way, the current behavior doesn't feel particularly elegant, requiring someone who just wants to quickly "ajaxify" a site to wrap all their ajax-loaded content in a parent element (that's not body).

comment:2 Changed 3 years ago by dmethvin

Maybe this is an issue of documentation.

You mean documentation like this? :)

jQuery uses the browser's .innerHTML property to parse the retrieved document and insert it into the current document. During this process, browsers often filter elements from the document such as <html>, <title>, or <head> elements. As a result, the elements retrieved by .load() may not be exactly the same as if the document were retrieved directly by the browser.  http://api.jquery.com/load/

I agree it would be ideal if we could somehow load complete document HTML (somewhere off the current document, without fetching its linked images/css) and inject just parts of them into the current document. There are some workarounds like loading the doc into an iframe or fetching it as an XML document, but they have their own sets of problems and don't work for the general case either.

See also #7757 .

comment:3 Changed 3 years ago by cowboy

Also, given this test.html:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
  <title>Index page</title>
</head>
<body>
  <div id="content">
    <p>stuff</p>
    <p>more stuff</p>
  </div>
</body>
</html>

If I do this:

$('<div/>').load( 'test.html #content', function(){
  console.log( this );
} );

I get this, which should be expected:

<div>​
  <div id=​"content" class=​"content">​…​</div>​
</div>​

Bit if I use $.get instead of using $.fn.load plus a string selector, this (extremely unintuitive) behavior is what I observe:

$.get( 'test.html', function(html){
  console.log( $(html).find( '#content') ); // fails!
  console.log( $(html).filter( '#content') ); // works!
});

Of course, this works.. but it's also fairly inelegant:

$.get( 'test.html', function(html){
  console.log( $('<div/>').html( html ).find( '#content' ) ); // ugleey!
});

comment:4 Changed 3 years ago by cowboy

A user could do something like this, however:

$.get( 'test.html', function(html){
  html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){
    return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>';
  });
  
  // These all behave as-expected:
  console.log( $( html ).find( '#content' ) );
  console.log( $( html ).find( '[data-element=head]' ).children() );
  console.log( $( html ).find( '[data-element=body]' ).contents() );
});
Last edited 3 years ago by cowboy (previous) (diff)

comment:5 Changed 3 years ago by snover

  • Status changed from new to closed
  • Resolution set to duplicate

comment:6 Changed 3 years ago by snover

Duplicate of #3409.

Please follow the  bug reporting guidlines and use  jsFiddle when providing test cases and demonstrations instead of pasting the code in the ticket.

View

Add a comment

Modify Ticket

Action
as closed
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.