jQuery selector is overlooking elements from a separate context - javascript

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');

Related

How to hide specific sentence on page load with Javascript

I need to hide a specific sentence "The leader of tomorrow, are great" from the content below without calling any class or div.
Example:
<p>The leader of tomorrow, are great leaders</p>
<br>
The leader of tomorrow, are great leaders of tomorrow.
Any solution to this would be appreciated.
Why does jQuery('*:contains("sentence to hide")') not work on this scenario?
It will match all elements that contain the sentence. So it will match the closest node but in addition all parent nodes all the way up. So instead of matching element eg in this case the "P-node" it will also match for body, html and even the script itself if it is inline because it also contains the sentence! And if the element that actual contains the sentence to hide is wrapped by lets say:
<div id="_1">
<div id="_2">
<div id="_3">
<p id="_4">sentence to hide</p>
</div>
</div>
</div>
Then with jQuery('*:contains("sentence to hide")') you will get a jQuery collection with [{html}, {body}, {#_1}, {#_2}, {#_3}, {#_4}]
Here is another approach...
jQuery( document ).ready(function(){
jQuery('body').html(function(iIndex, sHtml) {
return sHtml.replace(/The leader of tomorrow, are great/g, '');
});
});
Or to have more control you can wrap the sentence and style the wrapper as you like...
jQuery( document ).ready(function(){
var sNeedle = 'The leader of tomorrow, are great';
jQuery('body').html(function(iIndex, sHtml) {
return sHtml.replace(
new RegExp(sNeedle, 'g'),
'<span class="hide-specific-sentence">' + sNeedle + '</span>'
);
});
});
and then in your CSS:
.hide-specific-sentence{
display: none;
}
NOTES:
I would not recommend to treat web content like this but if you have to for whatever reason you can do it this way
narrow the selcetor to the closest possible parent that actual contains the sentence(s) maybe ".content" or whatever
make shure that you do this action before anything else because
attached event handlers could get lost (depending on the way they are bound)
If you try the code as SO snippet you will get an error. This is propably due to some internal restrictions (I guess). But I tried it local and it works like a charme or you can try with this working PEN that also works like a charme...
jQuery has the contains-method. Here's a snippet for you:
<script type="text/javascript">
$(function() {
var result = $('*:contains("The leader of tomorrow, are great")');
});
</script>
The selector above selects any element that contains the target string. The result will be a jQuery object that contains any matched element. See the API information at: http://docs.jquery.com/Selectors/contains#text
One thing to note with the '*' wildcard is that you'll get all elements, including your html and body elements, which you probably don't want. That's why most of the examples at jQuery and other places use $('p:contains("The leader of tomorrow, are great")')

Can't get to the DOM element

I got voting script on my page (jQuery Raty), everything work's fine but I want to change rate number in my ajax after voting.
Have problem with getting to span class .glosy
My html:
<div class=\"movie__rate\">
<div class=\"score\" data-score=\"$czy_glosowal2[ocena]\" data-user=\"$stat[id]\" data-movie=\"$podobny_film[id]\"></div>
<span class=\"movie__rate-number\">
<span class=\"glosy\">170</span>
głosów</span>
</div>
My JS:
('.score').raty({
click: function(score) {
var licznik_komentarzy = $(this).find('.glosy').text();
var zapisanie_licznika = $(this).find('.glosy').val(licznik_komentarzy);
var dodanie_licznika = +$(this).find('.glosy').val() + 1;
$(this).find('.glosy').text(dodanie_licznika);
}
});
tried different methods (next(), nextAll(), find()) and still cant figure it out.
Assuming that this is actually the element (depends on how plugin is written) ... you are looking in the wrong direction in the DOM tree.
closest() looks up the tree for ancestors.
You would need to use find() to look for a descendant of .score

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>

jQuery: selectors not working over ajax downloaded page

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

Categories

Resources