jquery find() method - javascript

Please consider the following code :
<!DOCTYPE html>
<html>
<style>
p{ width:200px; }
</style>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<p><span>Hello</span>, how are you?</p>
<p>Me? I'm <span>good</span>.</p>
<script>
$(document).ready(function(){$("p").find($("*")).andSelf().css("background- color","blue"); });
</script>
</body>
</html>
The output is the whole document turning into blue color while I only wanted the paragraph and span inside it to turn blue.
If I use $("p").find(" * ") instead of $("p").find($(" * ")) then everything shows according to my need. Can anyone work out the difference between the two approach?Thanks!
Note: Please everyone note that I know there are easier methods to do this stuff,but I just want to know why this didn't work..

Disclaimer: The other answers already suggest better selectors to
achieve your goal, but I understand you want to know why andSelf()
ends up matching all the elements in the document, so I'll try to
explain that.
First, as you know, andSelf() adds the previous set of elements on the stack to the current set. So, in your case, it seems it should add the <p> elements to the set containing their descendants:
$("p") // match the paragraphs
.find($("*")) // match all the elements that descend from a paragraph
.andSelf() // add the paragraphs to the elements above
However, the above assumes that find($("*")) is the previous set of elements, and that's simply not the case here. The first hint about this comes from the documentation for find():
As of jQuery 1.6, we can also filter the selection with a given jQuery
collection or element. With the same nested list as above, if we start
with:
var $allListElements = $('li');
And then pass this jQuery object to find:
$('li.item-ii').find( $allListElements );
This will return a jQuery collection which contains only the list
elements that are descendants of item II.
The last sentence is particularly interesting: it seems to imply that the jQuery object passed to find() is filtered in order to match the descendants of the elements in the original set. If that's indeed the case, the logic would be inverted, and the previous element set would end up being $allListElements instead of the set returned by find().
A look at the jQuery source code shows that's exactly what happens:
find: function(selector) {
var self = this, i, l;
if (typeof selector !== "string") {
return jQuery(selector).filter(function() {
for (i = 0, l = self.length; i < l; i++) {
if (jQuery.contains(self[i], this)) {
return true;
}
}
});
}
// [...]
}
So, when you write:
var elements = $("p").find($("*")).andSelf();
You're actually writing the equivalent of:
var self = $("p"), i, l;
var elements = $("*").filter(function() {
for (i = 0, l = self.length; i < l; i++) {
if ($.contains(self[i], this)) {
return true;
}
}
}).andSelf();
As you can see, the previous element set is actually $("*") instead of the set returned by find($("*")) because of the logic inversion. Therefore, all the elements in the document end up being legitimately added to the current set by andSelf().

You just need
$("p").css("background-color","blue");
To change the color or all the p tags in the document. Is there some specific reason for the way you have done it?

The $("*") has no context, so it selects every element in the document. You want to find all elements within the currently selected element, so you need to just pass the string to find.
However, it's completely unnecessary and you could just apply the style to the p (as the span is a child you don't to apply it to that too):
$("p").css("background-color","blue");
Note that in that line above I've used background-color with no spaces, unlike in your question. I'm guessing it was just a typo when you wrote the question, but it won't work if you put spaces in the property name.

You don't have to do any of that. Just do this.
$(function() { $("p").css('background-color', 'blue'); });
Note: $(function() {}); is the same as $(document).ready(function(){});
Edit: Since you have two, you may have to do this:
$(function() { $("p").each(item, function() { item.css('backround-color', 'blue'); })});
Edit2: Based on your comments, you want this:
$(function() { $("p").find('span').andSelf().css('background-color', 'blue'); });

The difference between $("p").find(" * ") and $("p").find($(" * ")) is that in the second one you're passing find() a jquery object instead of a regular selector string.
EDIT: I just tried it out. Looks like adding the andSelf() makes it select the entire document somehow. The logical process would be select p > find all elems inside that match everything in the document > select self(p) > color, but it seems to be going wrong at the select self bit.
I think it(the andSelf()) just selects the object passed to find(), which is $('*'), and so selects everything.

If you want to select all P's simply use
$(document).ready(function(){
$("p").css("background-color","blue");
});
No reason to complicate stuff
If you want to select the spans inside you can do something like
$(document).ready(function(){
$("p > span").css("background-color","blue");
});
** Update **
Your selector attribute in your find query is bad, you shouldn't have it like $("*") but only "*". However the $("p").find("*")... will only select any elements inside the <p> tag so trailing the find method with an andSelf will make the selection ambiguous.

Related

jQuery find not working in top-level elements if not added to DOM

I'm loading some HTML with jQuery asynchronously:
$.get(path, {}, function (data) {
var result = $(data);
var resultSelector = result.find(selector);
});
result is a valid HTML that contains my selector (in my specific case, "#UsersAndRolesResults").
I can see that it contains when I simply type result into the console, it's there exactly with the same ID, no typos or anything.
However, result.find(selector) returns 0 elements.
In my specific example, this is result:
And:
Why?
UPDATE: I can query for other elements that are inside #UsersAndRolesResults with their ID tag and they are returned correctly. I also cannot query any other top-level elements in the result. I think there is an issue with querying top-level elements inside the result.
UPDATE 2: Just try $('<div id="target"></div>').find("#target") and you will get 0 results, where you should obviously get the div.
No. This is not bug, this is behavior defined in jQuery.
find() is used to select the elements which are inside the element on which find is called. In your case, the element is not children of the selector, so find will not work.
Example:
<div id="container">
<div class="message">Hello World!</div>
<div>Bye World</div>
</div>
JavaScript:
$('#container').find('.message');
This will select the element having class message and which is inside the element having ID container.
But, if you use find, it'll return nothing i.e. empty array since there is no element #container inside #container.
$('#container').find('#container');
Your code is equivalent to this ^^^.
If you want, you can use filter. filter will go through each element and check if this matches the selector, if it then the element is added to the result set.
$('#container').filter('#container');
This'll give you complete element.
It seems to be a design decision with jQuery. Top-level elements in an AJAX result are not queried correctly with find. Interesting.
I've solved my problem with a workaround by creating a dummy div element, encapsulating my result inside that element, and then querying that dummy element. It worked:
var t = $("<div>")
t.append(result);
t.find("#UsersAndRolesResults"); //this one returned my object correctly
For a simple example, try:
$('<div id="target"></div>').find("#target");
You will get 0 results.
Try:
$('<div><div id="target"></div></div>').find("#target")
And you'll get the correct result.
Try this:
$.get(path, {}, function (data) {
var result = $($.parseHTML(data));
var resultSelector = result.find(selector);
});
Given
console.log($("<div id=target></div>").find("#target"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
.length would be 0 as expected.
You can use .closest() to search for both child elements and original jQuery object selector
console.log($("<div id=target></div>").closest("#target"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

select html object without id

Edit: one missing piece of information - I can't use the class selector because there are more divs with the same class. I already thought of that, but I forgot to mention it. I have no idea why my post got downvoted, but it seems awfully silly considering I provided a lot of information, gave it honest effort, and tried to be verbose with code examples. People on this forum are ridiculous sometimes.
I'm trying to set the id of a div that doesn't have one and there's no way I can give it one upon generation of the page. I've tried using jquery (.each, .contains, .find, .filter, etc.) and I can't seem to get it right. I know a ton of people have asked this question, but none of the answers made sense to me.
I have the ability to set the text (html?) of the div, but nothing else. It ends up looking like this:
<div class="dhxform_note" style="width: 300px;">Remaining letters: 500</div>
I want a handle to the div object so I can show the user how many more letters they can type by updating the text.
Using this:
$("div")
returns a list of all divs on the page. I can see the target div in the list, but I can't get jquery to return a single object.
I know it can also be done with something like this:
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++) {
if( /^Remaining letters/.test(divs[i].innerText) )
divs[i].id = "kudosMsgNote"
}
}
but I was hoping to complete this with a cleaner looking solution involving jquery. I also simply want to know how to do it with jquery, aesthetics not withstanding.
Use a class selector.
var theDivViaTheClass = $(".dhxform_note");
Class Selector (“.class”)
Description: Selects all elements with the given class.
version added: 1.0
jQuery( ".class" )
class: A class to search for. An
element can have multiple classes; only one of them must match.
For class selectors, jQuery uses JavaScript's native
getElementsByClassName() function if the browser supports it.
You seem to be targeting the <div> by its text. Try using the :contains selector:
$("div").filter(':contains("Remaining letters")').first().attr("id", "kudosMsgNote");
The .first() is to make sure you don't set the same id for multiple elements, in case multiple elements contain the text "Remaining letters".
Here's the docs for the :contains selector: http://api.jquery.com/contains-selector/
Be careful, the text you're looking for is case sensitive when using :contains!
Is that div the only one with the class dhxform_note? If so, you can use the class selector:
$('.dhxform_note').html();
With jQuery, you can specify any css selector to get at the div:
$(".dhxform_note").attr("id", "kudosMsgNote");
will get you this element as well.
Selecting on inner text can be a bit dicey, so I might recommend that if you have control over the rendering of that HTML element, you instead render it like this:
<div name="remainingLetters" class="dhxform_note" style="width: 300px">Remaining Letters: 500</div>
And get it like this:
$("[name=remainingLetters]").attr("id", "kudosMsgNote");
However, it's possible that you really need to select this based on the inner text. In that case, you'll need to do the following:
$("div").each(function() {
if ( /^Remaining letters/.test($(this).html()) ) {
$(this).attr("id", "kudosMsgNote");
}
});
If you cannot set id for whatever reason, I will assume you cannot set class either. Maybe you also don't have the exclusive list of classes there could be. If all those assumptions really apply, then you can consider down your path, otherwise please use class selector.
With that said:
$("div").filter(function() {
return /^Remaining letters/.test($(this).text())
}).attr('id', 'id of your choice');
For situations where there are multiple divs with the class dhxform_note and where you do not know the exact location of said div:
$("div.dhxform_note").each(function(){
var text = $(this).text();
if(/^Remaining letters/.test(text)){
$(this).attr("id", "kudosMsgNote");
}
});
EXAMPLE
If, however, you know that the div will always be the 2nd occurrence of dhxform_note then you can do the following:
$("div.dhxform_note").get(1).id = "kudosMsgNote";
EXAMPLE
Or do a contains search:
$("div.dhxform_note:contains('Remaining letters')").first().attr("id", "kudosMsgNote");
EXAMPLE

adding classes (serially numbered) to each DIV on page automatically via javascript?

i'm running a contact-form-plugin on my wordpress installation. everything works fine but i want to style my form a little bit more. to do it like this i have to style some DIVs (which the plugin is processing) in a different way. the problem is: all DIV-containers have no IDs or classes! everything is processed by the plugin and my PHP-skills are like zero so i have to deal with this "naked" DIVs ;-)
the question is: is it possible to add serially numbered classes to each DIV on the current site via javascript?
thanks so far and i hope you get waht i mean (sorry for that shitty english)!
Yet another way, passing a callback to .attr [docs]:
$('div').attr('class', function(i) {
return 'someClass' + i;
});
Though you could also use this for IDs instead, since each class will be unique.
Note that you don't have to use IDs or classes to select elements, there are a number of selectors and traversal methods with which you can get a reference to the elements you want.
To prevent overriding existing classes, either select only the elements with no class or somehow narrow down the selection of divs to only those you want:
$('div:not([class])')
or use .addClass [docs] instead of .attr:
$('div').addClass(function(i) {
return 'someClass' + i;
});
You need something like this,
Live Demo
$('div').each(function(i, item){
$(this).attr('class', "YourClass" + i); //You can replace i with some other variable if required and increment it.
});
You could do this:
var divs = document.getElementsByTagName("div");
var className = "myClass";
for( var i = 0, max = divs.length; i< max; i++ ){
divs[i].setAttribute("class",className + i.toString());
}

Need jQuery text() function to ignore hidden elements

I have a div set up something like this:
<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>
EDIT: To clarify, this is the simplest example. The div could have any arbitrary number of n deep nested children.
$('#test').getText() returns 'Hello Goodbye'. Here's a one liner to test in Firebug: jQuery('<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>').text()
This seems to be because what jQuery uses internally, textContent (for non IE), returns hidden elements as part of the text. Hrmph.
Is there a way to return the text content ignoring display:none'd elements? Basically I am trying to mimic the text you would get from highlighting the div with your mouse and copying to system clipboard. That ignores hidden text.
Interestingly, if you create a selection range and get the text from it, that also returns text inside display:none elements.
var range = document.body.createTextRange();
range.moveToElementText($('#test')[0]);
range.select();
console.log(range.toString()); // Also logs Hello Goodbye!
So creating a document selection range doesn't appear to do the same thing as highlighting with the mouse in terms of display:none elements. How do I get around this dirty pickle conundrum?
Edit: using .filter(':visible').text has been suggested, but it won't work for this scenario. I need the returned text to be EXACTLY what would come from a selection with the mouse. So for example:
$('<div>test1 <p>test2</p>\r\n <b>test3</b> <span style="display:none">none</span></div>').appendTo(document.body).children().filter(':visible').text()
returns
"test2test3"
When the output I actually want is
test1 test2
test3
linebreaks, whitespace and all, which come from the \r\n
Filter the elements using .filter(":visible").
Or use this:
$("#test :visible").text();
But the jQuery documentation advises us to use .filter() instead:
Because :visible is a jQuery extension and not part of the CSS specification,
queries using :visible cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :visible to select elements, first select the elements using a pure CSS selector, then use .filter(":visible").
Use :visible in your selector as such:
$("#test > p:visible").text()
A Function example:
-- Edit:
http://jsfiddle.net/8H5ka/ ( Works on Chrome it displays "Hello" in Result )
If the above doesn't work:
http://jsfiddle.net/userdude/8H5ka/1/
If space isn't a major concern you could copy the markup, remove the hidden elements, and output that text.
var x = $('#test').clone();
x.filter(':not(:visible)').remove();
return x.text();
I had this problem and found this question, and it seems the actual solution is based on the provided answers but not actually written out. So here's a complete solution that worked for my situation, which is the same as the OP with the additional provision that elements may be invisible due to external styles based on DOM position. Example:
<style>.invisible-children span { display: none; }</style>
<div class="invisible-children">
<div id="test">Hello <span>Goodbye</span></div>
</div>
The solution is to:
Make a clone of the entire object.
Remove invisible objects in place; if we take #test out of the DOM before we remove invisible objects, jQuery might not know they're invisible because they will no longer match the CSS rules.
Get the text of the object.
Replace the original object with the clone we made.
The code:
var $test = $('#test');
// 1:
var $testclone = $test.clone();
// 2: We assume that $test is :visible and only remove children that are not.
$test.find('*').not(':visible').remove();
// 3:
var text = $test.text();
// 4:
$test.replaceWith($testclone);
// Now return the text...
return text;
// ...or if you're going to keep going and using the $test variable, make sure
// to replace it so whatever you do with it affects the object now in DOM and
// not the original from which we got the text after removing stuff.
$test = $testclone;
$test.css('background', 'grey'); // For example.
Here is how I did it with MooTools:
$extend(Selectors.Pseudo, {
invisible: function() {
if(this.getStyle('visibility') == 'hidden' || this.getStyle('display') == 'none') {
return this;
}
}
});
Element.implement({
getTextLikeTheBrowserWould = function() {
var temp = this.clone();
temp.getElements(':invisible').destroy();
return temp.get('text').replace(/ |&/g, ' ');
}
})
I search for that and found this question but without solution.
Solution for me is just get out of jquery to use DOM:
var $test = $('#test').get(0).innerText
or if more than on element in array of selector, you need a for loop and a merge but I guess that most of time it is the first version that you need.
var $test = $('#test').get().map(a => a.innerText).join(' ');

reach a HTML element using javascript without getElementById

I'm new to javascript. I created this div called colorme. I can successfully color it via javascript. Now assuming i want to change the background of <p>...</p>, or <span>,etc how do i reach it via Javascript? (no jquery).
Like document.getElementById() would work on the div and i reach it. Now i cannot keep giving unique id's to all the elements. How do i reach the inner elements like <p> or <span>, etc?
<div id="colorme">
<p>Blah Vblah Blah Content</p>
<span>Blah Vblah Blah Content</span>
</div>
You can use the element that you've found as a context for getElementsByTagName.
var colorme = document.getElementById('colorme'),
spans = colorme.getElementsByTagName('span');
Note that spans is a NodeList -- similar to an array -- containing all the span elements within colorme. If you want the first one (indeed, the only one in your code sample), use spans[0].
You should check out the many DOM traversal functions provided in standard javascript.
Tutorial: http://www.quirksmode.org/dom/intro.html
Reference: http://reference.sitepoint.com/javascript/Node
and http://reference.sitepoint.com/javascript/Element
Although the answers do give good ways to do it for this specific case....
The issue you're facing is called DOM-traversal. As you know, the DOM is a tree, and you can actually traverse the tree without knowing in advance the element id/type/whatever.
The basics are as follows
el.childNodes to access a list of children
el.parentNode to access the parent element
nextSibling and previousSibling for next and previous sibling nodes
For further info, see [MDC DOM pages](
Here are three ways:
If you only care about decent browsers, document.querySelector (returns the first matching node) and document.querySelectorAll (returns a NodeList) - e.g. document.querySelector('#colorme p').
HTMLElement.getElementsByTagName() (returns a NodeList) - e.g. document.getElementById('colorme').getElementsByTagName('p')[0]
HTMLElement.children, etc. - document.getElementById('colorme').children[0] (.firstChild will probably be a text node, lots of fun DOM stuff to get into there, the quirksmode DOM intro linked to is good stuff).
It's quite simple: getElementsByTagName()?
You could use getElementsByTagName()
Loop through the children:
var div = document.getElementById('colorme');
var i, l, elem;
for (i = 0, l = div.childNodes.length; i < l; i++) {
elem = div.childNodes[i];
// Check that this node is an element
if (elem.nodeType === 1) {
elem.style.color = randomColorGenerator();
}
}
In this case you can use:
var colormeDiv = document.getElementById('colorme');
var e1 = colormeDiv.getElementsByTagName('p');
var e2 = colormeDiv.getElementsByTagName('span');
to get the two elements inside 'colorme' div.
getElementById is just one of JavaScript's DOM methods. It returns an HTMLElement DOM object which you can then query to find child, parent and sibling elements. You could use this to traverse your HTML and find the elements you need. Here's a reference for the JavaScript DOM HTMLObject.
[after answering, I realised this is no answer to your fully explained question, but it is the answer to the question raised in the title of your post!]
One nice way of doing this is declaring a global var on the top of your Javascript that refers to the document, which can then be used everywhere (in every function):
<html>
<head>
<script type="text/javascript">
// set a global var to acces the elements in the HTML document
var doc = this;
function testIt()
{
doc.blaat.innerHTML = 'It works!!';
}
</script>
</head>
<body>
<div id="blaat">Will this ever work..?!</div>
<button onclick="testIt()">Click me and you'll see!</button>
</body>
</html>
As my first impression when I got to 'getElemenyById()' was that it sounds like a function that will iterate through the DOM's element list until it finds the element you need; this must take some time. With the above example, you simply access the element directly.
I'm not sure if I'm really saving CPU / adding speed this way, but at least it feels that way :)

Categories

Resources