jQuery: selectors not working over ajax downloaded page - javascript

I'm trying to extract an element from an ajax downloaded page to later append it to the DOM. I'm fetching the page like this:
$.ajax({
url: pagePath,
success: function (data) {
//data is correctly shown in debugger, all the elements exist.
var $div = $(data).find("[data-custom-attr]").first();
//$(data) has lenght > 0, however $div has length 0!
//$(data).find("#ajaxpage") also has lenght 0!
},
dataType: "html"
});
This is the fetched page:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="ajaxpage" data-custom-attr="ajaxpage">
<h2>Ajax downloaded page!</h2>
</div>
</body>
</html>
I've tried with a few different selectors and everyone fails. I feel like there must be a really silly mistake somewhere in the code but can't see it.
What is the problem with those selectors?

The reason for this is that jQuery throws away all the nonsense you've given it, and only parses the div with h2:
$("<!DOCTYPE html><html><head></head><body><div id='ajaxpage' data-custom-attr='ajaxpage'><h2>Ajax downloaded page!</h2></div></body></html")
is exactly the same as
$("<div id='ajaxpage' data-custom-attr='ajaxpage'><h2>Ajax downloaded page!</h2></div>")
(jQuery's $("...") doesn't create full documents, it'll only build DOM fragments)
Since the first element is already your div, you cannot find [data-custom-attr] in its subtree, so the result will be []. If you do a find("h2"), that'll work fine, since it's in the subtree of your div, but you can't find the div itself.

var $div = $('<div/>').html(data).find("[data-custom-attr]").first();

Try this:
var $div = $('<div></div>').html(data).find("[data-custom-attr]").first();

Related

jQuery selector is overlooking elements from a separate context

I'm making an ajax request to a page on my site with this element as a direct child of the body tag:
<div class="container" id="wantme"><div class="content"></div></div>
There's only one .container, and I want to grab its ID which I don't know.
As far as I can tell, this code should do what I want:
$.get('/page', function(data) {
id = $('.container', data).attr('id');
});
But the .container selector fails to find anything.
I did find these two workarounds. I can find .content, and I can climb up the tree like this:
id = $('.content', data).parent().attr('id');
But I can't leap directly there.
I found this workaround elsewhere on StackOverflow that works:
html = $('<div></div>').html(data);
id = html.find('.container').attr('id');
But why is it that the seemingly obvious answer doesn't work?
UPDATED ANSWER: I'll leave my original answer at the bottom, however I'm concerned it may misbehave depending on browser. jQuery's .html() makes use of Javascript's innerHTML - some browsers choose to strip <head> and <body> tags when using innerHTML, whereas others do not.
The safest method to achieve what you're after may still be the workaround you mentioned, like so:
var data = '<!doctype html><html><body><div class="container" id="findme"><div class="content"></div></div></body></html>';
var $container = $("<div />").html(data).find(".container");
var id = $container.attr("id");
console.log(id);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
More information as to the browser-related issues can be found here.
PREVIOUS ANSWER:
When you pass HTML to a jQuery element, it will ignore the <body> tags, as well as anything outside of them. Given the data string in your JSFiddle, $(data) will create something that looks like this:
<div class="container" id="findme">
<div class="content"></div>
</div>
As you can see in the HTML above, your .container isn't inside of $(data) - it is $(data).
Because $(data) is representing your .container element, you should just be able to do $(data).attr("id") to retrieve what you're after.
var data = '<!doctype html><html><body><div class="container" id="findme"><div class="content"></div></div></body></html>';
var id = $(data).attr('id');
console.log(id);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You are not getting the ID from $('.container', data).attr('id'); is because you are setting the value of the second parameter. What you want to do is this: $('.container ' + data).attr('id');.
Update:
If data is a string then you should convert it into a DOM element: $('.container', $(data)).attr('id');

Why can't I use part of this AJAX response?

I have a jQuery ajax call:
$.ajax ({
url : 'foo/bar/',
success: function(data) {
console.log(data);
var newHeaderText = $(data).find('#header-text').text();
$("#header-text").text(newHeaderText);
var newContent = $(data).find('#content').html();
console.log(newContent);
$("#content").html(newContent);
}
});
The logged data successfully shows the complete html of the target page.
The header-text section works as expected.
The console.log(newContent), however, returns undefined. Short of posting the entire html of foo/bar/ here, suffice it to say that I am quite sure a div of id content is in data, and that it has many child elements.
Why is my attempt at grabbing the content of one page and plopping it into the content of another page not working. It is especially confusing to me as header-text seems to work just fine.
You could try $.parseHTML instead:
var newContent = $($.parseHTML(data)).find('#content');
If #content is a top-level element, then the above code might still miss it. You could then solve this by first loading the HTML in container, and then find the element in there:
var newContent = $('<div>').append($.parseHTML(data)).find('#content');

jQuery "add" Only Evaluated When "appendTo" Called

this has been driving me crazy since yesterday afternoon. I am trying to concatenate two bodies of selected HTML using jQuery's "add" method. I am obviously missing something fundamental. Here's some sample code that illustrated the problem:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<p id="para1">This is a test.</p>
<p id="para2">This is also a test.</p>
<script>
var para1 = $("#para1").clone();
var para2 = $("#para2").clone();
var para3 = para1.add(para2);
alert("Joined para: " + para3.html());
para3.appendTo('body');
</script>
</body>
</html>
I need to do some more manipulation to "para3" before the append, but the alert above displays only the contents of "para1." However, the "appendTo appends the correct, "added" content of para1 and para2 (which subsequently appears on the page).
Any ideas what's going on here?
As per the $.add,
Create a new jQuery object with elements added to the set of matched elements.
Thus, after the add, $para3 represents a jQuery result set of two elements ~> [$para1, $para2]. Then, per $.html,
Get the HTML contents of the first element in the set of matched elements or set the HTML contents of every matched element.
So the HTML content of the first item in the jQuery result ($para1) is returned and subsequent elements (including $para2) are ignored. This behavior is consistent across jQuery "value reading" functions.
Reading $.appendTo will explain how it works differently from $.html.
A simple map and array-concat can be used to get the HTML of "all items in the result set":
$.map($para3, function (e) { return $(e).html() }).join("")
Array.prototype.map.call($para3, function (e) { return $(e).html() }).join("")
Or in this case, just:
$para1.html() + $para2.html()
Another approach would be to get the inner HTML of a parent Element, after the children have been added.

Deleting and inserting Code in a DIV via jQuery

I know this has been adressed before, but I can't seem to get it working for me.
I am trying to create a football pitch with editable players via HTML/JavaScript/jQuery.
I can produce the field the first time when loading the page without any problems. The code looks like this:
<div id="pitch" class="updateAble">
<script type="text/javascript">
appBuilder(team1, team2);
</script></div>
appBuilder() looks like this:
var appBuilder = function (team1, team2) {
team1.Display();
team2.Display(); }
It simply creates the players on the pitch for both teams. As it does. I now want to push an input-button to call a function appUpdate(), which deletes the content of #pitch and puts the appBuilder()-part in again as to renew it (if I changed or added players):
var appUpdate = function () {
var newContent = "<script type='text/javascript'>appBuilder(team1, team2);</script>";
var updateItem = $('#pitch');
updateItem.empty();
updateItem.append(newContent);}
Here is what drives me nuts: It seems to work just fine up to and including the empty()-function. So the code has to be fine.
But when I try to append newContent to the #pitch-DIV, the programm seems to completely delete everything inside <head> and <body> it recreates a clean html-file (with empty html-, head-, body-tags) and inserts my players inside <body>.
Any ideas as to why it is doing that?
Thanks in advance!
UPADTE: The solution was a rookie mistake (which is fitting, since I'm a rookie). The Team.Display()-method was trying to do a document.write() call. As I learned: If you call document.write once the document is fully loaded, it will delete your site. Thanks to jfriend for the solution! :)
If you call document.write() AFTER the document has finished loading, then it will clear the current document and create a new empty one.
What you need to do is use DOM insertion operations rather than document.write() to add/change content in the DOM once the document has already loaded.
My guess is that the .Display() method is using document.write() and you need to change the way it works to insert content into a parent node rather than write it into the current position.
Some ways to insert content:
var newNode = document.createElement("div");
node.appendChild(newNode);
node.innerHTML = "<div>My Content</div>";
Or, if you're using jQuery, you can use it's wrappers for this:
obj.append("<div>My Content</div>");
obj.html("<div>My Content</div>");
.html() would empty and fill the div at once. Have you tried that ?
updateItem.html(newContent);
I proposed a JQuery replacement for your code that does what you want, ion the style of your own typing.
Note that I kept the .html() call to mimic your "empty()" function, but it is not necessary. Simply put he code in the append, straight into the html() et get rid of the extra unnecessary remaing bit of code.
My code replacement, as a 100% functioning .html file. Hope it helps, cheers.
<html>
<header>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
var appBuilder = function (team1, team2) {
//team1.Display();
//team2.Display();
}
var team1, team2;
</script>
</header>
<body>
<div id="pitch" class="updateAble">
<script type="text/javascript">
appBuilder(team1, team2); // Original code to be updated
</script>
</div>
<script>
var appUpdate = function () {
$("#pitch").html("<!-- Old javscript code has been flushed -->").append($("<script />", {
html: "appBuilder(team1, team2); // brand new, replaced code"
}));
}
appUpdate();
</script>
</body>
</html>

Parsing using mrss

I'm trying to parse an mrss feed using jquery but am having some difficulty targeting the child element.
Code:
$(xml).find("item").each(function(){
var $item = $(this);
alert($item.find("media\\:thumbnail").text();
});
MRSS Structure:
<media:thumbnail url="http://somewebsite.com/someimage.jpg" />
UPDATE
The solution $item.find("media\:thumbnail").attr("url") works very well in Firefox but running the code in Chrome reveals an undefined value. Can someone suggest a workaround.
Thanks
You're trying to display the text() of your found nodes. The example node you include has no text at all. You want the value of the url attribute:
$(xml).find("item").each(function(){
var $item = $(this);
alert($item.find("media\\:thumbnail").attr("url"));
});
If your media:thumbnail element had something between opening and closing tags you could use text() (and you still wouldn't be getting the url value):
<media:thumbnail url="http://somesite.com/simeimage.jpg">Some text string</media:thumbnail>

Categories

Resources