I am confused about these lines of JQuery:
if ($(ui).hasClass("color1"))
$(ui).removeClass("color1").addClass("color2")
else
$(ui).removeClass("color2").addClass("color1")
in this code.
I know that $(ui) is creating a JQuery instance. I would like to know if
.hasClass is testing the entire DOM tree of the JQuery instance for
any element which has color1 as part of its class attributes.
Also, the docs for
removeClass do not state what
removeClass returns. They do so allegorically with this code:
$("p").removeClass("myClass noClass").addClass("yourClass");
But I would prefer an explicit statement about what removeClass()
returns. Because my second questions is: what is returned by removeClass and how is addClass can making use of it?
It returns the jQuery object. See the documentation of the removeClass function: http://api.jquery.com/removeClass/
And the jQuery object: http://api.jquery.com/Types/#jQuery
Excerpt from the documentation of the jQuery object:
A jQuery object contains a collection of Document Object Model (DOM)
elements that have been created from an HTML string or selected from a
document. Since jQuery methods often use CSS selectors to match
elements from a document, the set of elements in a jQuery object is
often called a set of "matched elements" or "selected elements".
It returns the elements you selected using the query:
$("[some query here]").removeClass("...").text("the text that must be set");
So, adding class1 and removing class2 can be done like bellow:
$("query .class2")
.removeClass("class2")
.addClass("class1");
On the documentation page you can see: Returns: jQuery.
Look at the documentation for removeClass, it tells you Returns: jQuery
Related
I'm a rookie following an exercise to obtain the first element in the set of all img elements with an alt attribute. On p.35 of jQuery in Action 1st edition, I am entering the following commands in the "Wrapped Set Lab":
$("img[alt]").get(0)
or this:
$("img[alt]")[0]
both of these commands give this exception in the console:
error:TypeError Object# has no method 'addClass'
Here is the markup fragment:
<div>
<img src="images/image.1.jpg" id="hibiscus" alt="Hibiscus"/>
<img src="images/image.2.jpg" id="littleBear" title="A dog named Little Bear"/>
<img src="images/image.3.jpg" id="verbena" alt="Verbena"/>
<img src="images/image.4.jpg" id="cozmo" title="A puppy named Cozmo"/>
<img src="images/image.5.jpg" id="tigerLily" alt="Tiger Lily"/>
<img src="images/image.6.jpg" id="coffeePot"/>
</div>
Yet another post in SOsuggested this is normal and wrote to use the following instead:
$("img[ALT]").eq(0)
which works and returns 1 element in the wrapped set:
IMG#hibiscus
There is nothing in the errata section of the book for this. The responder to the SO post explains
"When you access an item from a collection with a subscript like [i], you're actually unwrapping it from the jQuery object, and accessing a raw DOM node, which doesn't have methods like addClass and css".
I am a rookie with jQuery and don't understand this error nor the explanation in a related SO post.
addClass is a jQuery method. It can only be called on jQuery objects. When you call [0] or .get(0) on your jQuery object, you are fetching the underlying DOM element. That object doesn't support the jQuery methods.
You should call addClass on the jQuery object itself:
img[alt].addClass('my-new-class');.
If you only want to add a class to the first image with an alt attribute, you could change the jQuery selector to $('img[alt]:first').
When you do something like $('img') what you get is a jQuery object. This jQuery object contains 0 or more DOM elements (in this case, img elements). The addClass method is a method of a jQuery object. It assigns a class to every element contained in the object.
But when you do $('img')[0], you are trying to retrieve the first DOM node contained in the jQuery object, if any. This node is not a jQuery object and so you can't use it to call methods from a jQuery object. You'd had to do something like
$('img').eq(0); //returns a new jQuery object with the element at position 0
or
$($('img')[0]);
which is basically another way of doing the same (jQuery can be constructed from a selector, another jQuery object, or native DOM elements.
Jquery
I am trying to change the inner text on multiple td element which I believe should look something like this although this does not appear to be a jquery object when I am debugging (I could be wrong).
What is the correct way this should be done?
$('.leg-number').each(function () {
this.html('foo');
});
Maybe try this instead:
$('.leg-number').html('foo');
which is a shorter and more efficient way to achieve your goal. It is just asking jQuery to set the inner html of every element with class "leg-number" to "foo" without any explicit iteration. Most of the jQuery methods like .html() can work on sets of elements so you don't really need to use .each() for simple cases like this.
Now on why your version didn't work: Using .each() would work if you wrapped this with the jQuery function $() so you could use the jQuery methods on it:
$('.leg-number').each(function () {
$(this).html('foo');
});
The variable this inside of the .each() callback is a DOM element and you need $(this) to convert it into a jQuery object that wraps this element. See the explanation in the answer by epascarello who explained it before I updated my answer.
Read the docs for each(). this is a DOM Html Element node, not a jQuery object reference. You can either convert it back to jQuery or use innerHTML directly.
$(this).html('foo');
or
this.innerHTML = 'foo';
The docs show using $(this) in the examples.
Change:
this.html('foo');
to:
$(this).html('foo');
You're attempting to use a jQuery method on a non-jQuery object. This of course assumes that your table cells have the class .leg-number.
As a example of jQuery code (https://coderwall.com/p/7uchvg), I read that the expression $('#foo a'); behaves like this:
Find every a in the page and then filter a inside #foo.
And it does not look efficient.
Is that correct? And if yes, how should we do that in a better way?
That is correct - Sizzle (jQuery's selector engine) behaves the same way as CSS selectors. CSS and Sizzle selectors are evaluated right-to-left, and so #foo a will find all a nodes, then filter those by nodes that descend from #foo.
You improve this by ensuring that your leaf selectors have a high specificity, usually by giving them a class or ID.
how should we do that in a better way?
Use the context parameter from jQuery.
$('a', '#foo');
Now jQuery will search all anchors within the context of the element with id: foo.
In your query the context is defaulted to document when omitted:
$('#foo a'); == $('#foo a', document);
In this case, your query is indeed not efficient.
You might take a look at this article.
While it is true that Sizzle is a right-to-left engine (which is the same way css is interpreted), it is not true that the specific selector in your example would select all anchor elements on the page and then filter their parents to match the id of "foo". Sizzle actually optimizes any selector that starts with an ID and uses that as the context for the entire selection, rather than using the document. In other words, the selector you've chosen basically translates to:
document.getElementById("foo").getElementsByTagName("a")
Really, that's not a bad selector at all.
However, given the other things jQuery needs to do (which includes looping over the elements to merge them onto the jQuery instance), jQuery("#foo").find("a") will always be the fastest because jQuery implements a jQuery object creation shortcut for id-only selectors, and then it does the find rooted from #foo.
In other words, Sizzle itself is not much different when doing Sizzle("#foo a") and Sizzle("a", document.getElementById("foo")), but jQuery("#foo").find... will be faster because of jQuery's own ID shortcut.
By the way, my remarks on Sizzle is assuming querySelectorAll is not being used. If it is, Sizzle just passes it on to qsa, which still isn't as fast as using jQuery's ID shortcut.
You can use find() for more granular control on your selector order:
$('#foo').find('a');
This will of course be more impressive with more complex selectors, where you can chain find() and filter().
For the record $('#foo').find('a') === $('a','#foo')
[Update] ok, I realized later that it's exactly what your link says...
The jQuery selector engine (Sizzle) has been refactored last year, you'll find detailed explanations here:
http://www.wordsbyf.at/2011/11/23/selectors-selectoring/
Instead of filtering with a inside #foo elements, simply attach a class to a elements and get a elements with class like $("a.class");. This would be more efficient.
Yet another "try it for yourself":
jsperf for various selectors on 10000 elements
jsperf for various selectors on 300 elements
jsperf for various selectors on a "more representative DOM"
Doesn't seem to be much difference with a "flat" DOM (1 & 2), but the performance varies much more with a nested DOM.
Also note that some of the test cases aren't selecting the correct elements (i.e. $('.a') vs $('.a', context)), but I left them from the original tests just for comparison.
This example will retrieve the all anchors elements a in an element called foo, to Find every a in the page and then filter a inside #foo as you want u should select a #foo
$("a #foo");
this will retrieve all the foo elements inside a elements.
Insofar as I can tell, the following code should work, creating a <div> element, and then creating a <p> element; the expression should result in a jQuery object with two elements:
$("<div>first element's content</div>").after("<p>second element's content</p>");
However, what I get is very different. The documentation (see the heading "Inserting Disconnected DOM Nodes") tells me the above code should result in a jQuery object, grabbing both HTML snippets and building the two DOM elements. But, what I've gotten, in several different versions of jQuery, all above 1.4, is a jQuery object with only 1 node. However, the following code works just fine, returning (what I believe is) the correct jQuery object, two elements inside:
$("<div></div>").after("<p>second element's content</p>");
And this example works as well:
$("<div></div>").after("<p>second element's content</p>").after("<p>third element's content</p>");
It seems the .after() method works fine if the first DOM node being created is empty, but does not when it is not (irrespective of the contents of subsequent DOM nodes being appended to it).
Am I missing something about jQuery's internals, quirky DOM issues and/or JavaScript peculiarities, or is this simply a jQuery bug that's persisted from version 1.4 on through 1.7?
(Here's a meager JSFiddle demonstrating the issue pretty plainly.)
This was a known bug in jQuery < 1.9. See http://bugs.jquery.com/ticket/8759
In jQuery >= 1.9, the following information from the upgrade guide should be noted:
Prior to 1.9, .after(), .before(), and .replaceWith() would attempt to add or change nodes in the current jQuery set if the first node in the set was not connected to a document, and in those cases return a new jQuery set rather than the original set. This created several inconsistencies and outright bugs--the method might or might not return a new result depending on its arguments! As of 1.9, these methods always return the original unmodified set and attempting to use .after(), .before(), or .replaceWith() on a node without a parent has no effect--that is, neither the set or the nodes it contains are changed.
Use add() to add objects to the collection. I use after() more in DOM elements that already exist or that are cached in a variable, but most of the time, if you work with dynamic markup is more practical to use the equivalent insertAfter().
$("<div>first element's content</div>").add("<p>second element's content</p>");
EDIT:
This works...
var $el = $('<div/>', {
text: 'hey there'
}).after('<p>Lorem</p>');
I found I was still sometimes having issues with .add() in place of .after(), so another easy way to get around this bug is to make a throw away wrapper element, .append() the sibling elements, and use .html() to just get the inner contents of the wrapper.
Example:
$('body').append(
$('<span/>').append(
$("<div>first element's content</div>")
).append(
$("<p>second element's content</p>")
).html()
);
This will add the <div> and <p> but discard the outer <span>. I have found this usually works fine with .append() but can have problems with .appendTo()
In jQuery 1.12.4, I found out that using a class selector instead of an ID selector solves this issue.
If you are struggling with
$("#myelement1").after("<p>test</p>"),
add a unique class to your element and try this:
$(".myelement1").after("<p>test</p>")
I'm trying to modify an HTML document, but its not the same as the DOM. However, I found that the syntax that I used with the DOM itself doesn't work with the other document. For example, I could find the title using $("title") with the DOM. However, with the document I have to use doc.find("title"). There are other, more involved changes and I'm stuck on how to proceed with this. Is there a reference I can look at? I know about the jQuery documentation, but that assumes that you're manipulating the DOM itself.
From what I've gathered the $ function assumes it's default scope for selectors is the active document. if you create jQuery objects that are not in the body of the document, the $(object).find(selector) is your best way to select within that object. The rest of the functions should work fine, at least the ones dealing with selecting, transversing and manipulation.
I should add that if the object is a jQuery object, there is no need to use the $. All the jQuery functions will be available to that object. So if you create:
var obj = $('<div>some text</div>');
you can use obj.addClass('cool) to get a jQuery object containing
<div class="cool">some text</div>
You can use jQuery on your doc by using the following
$("title", doc)
You pass the string "doc" to jQuery and it will use that instead of the DOM