I've read the this and this, but I'm trying to figure out how document.getELementbyClassName works so that I can re-implement it as an exercise. Obviously, I don't wan to just imitate the source code; my version will probably be much slower and more crude. I have a few questions, but any insight provided beyond my questions is appreciated.
When called on the document object, the complete document is searched, including the root node.
How does it search the entire document? Does this use some sort of regular expression?
document.getElementsByClassName('red test'); This is supposed to return all elements that have both red and test classes. But doesn't each element only have one class? Or is this to mean something like red orange test?
Is it correct that the elements are returned in an array? Something like [element1, element2, ...]. I'm not sure what is meant by "array-like."
Note: I'm new to JavaScript and have even less exerpeince with HTML, CSS, and jQuery.
To answer your questions:
Probably recursively. Otherwise there are plenty of different ways to traverse n-ary trees which is what the DOM is. Depth-first, breadth-first, however-you-want-first-really, they can all be implemented recursively, or alternatively use some data structure like a stack or a queue. How it does it really isn't important, what matters is how YOU think it should be done.
A simple algorithm for recursively identifying elements with those classes would be something like this
getByClassName(class, root) {
ret = []
if (root has class) {
ret.push(root);
}
for (each child of root) {
append getByClassName(class, child) to ret;
}
return ret;
}
Elements can have multiple classes. <a class="foo bar baz"> has classes foo,bar, and baz
All the Javascript methods like this operate on the DOM, not the HTML source code. When the HTML is loaded, the browser parses the HTML into the DOM, which is a data structure containing objects that represent the document contents. So it doesn't need to do pattern matching, it simply searches through the data structure for elements whose class list contains the specified class.
ELements can have more than one class, and this is very common. For instance, you might have a button class for all buttons, and an active class for active elements. The active button might then be <span class="button active">contents</span>.
The elements are returned in an HTMLCollection. This is an array-like object, so you can use elements.length to get the number of elements, and elements[i] to access a specific element in the collection. It's also a "live" collection, which means that if you change the DOM, the collection will automatically be updated to reflect the changes (e.g. if you remove the class from the object, it will no longer be in the collection).
Related
Ok so I finally have a code example to show this!
if ($('#Snowsports-row')[0].classList.contains("hidden") == false) {
$('#snowsports-only').removeClass("hidden")
}
The code works ONLY as written above, i.e., if the [0] were moved to the second line and removed from the first line, or if it were present/absent in both lines, it would fail.
I understand the output difference...
$('#Snowsports-row')
=> [<div>...]
$('#Snowsports-row')[0]
=> <div>...
...but I'm not understanding under what circumstances you're OK to get an array of element(s) and in which you need to tease out the exact element.
THANKS FOR ALL ANSWERS! Very clearly helped me to figure out that the problem may have been confusing JS/jQuery methods. Final version:
if ($('#Snowsports-row').hasClass("hidden") == false) {
$('#snowsports-only').removeClass("hidden")
}
The .classList method is not widely supported (not in MSIE 9.0 for example) so it's not portable, although where it exists it's fast.
Since every ID in a document is supposed to be unique, and since calling removeClass for a class that isn't present is harmless, just replace your entire call with:
$('#Snowsports-row').removeClass('hidden')
Or better yet, if that class means what I think it does, use .hide() and let jQuery do its job for you, potentially animation the transition in the process.
Alternatively, if you actually wanted to stick with using DOM and classList, you should use the .remove() method that classList already supports:
document.getElementById('#Snowsports-row').classList.remove('hidden')
although there's a minor disadvantage in that this code will crash if that element isn't found (since .getElementById will return null) whereas jQuery silently ignores calls made on empty selectors.
As for the meta-question - you use [n] if you want to access the single DOM element at position n within the jQuery object, as you've done when you use .classList.
You use .eq(n) to obtain a jQuery object representing that DOM element, e.g. if you want to apply jQuery methods to that (single) element.
If there's only a single element, or you want the jQuery method to apply to every matching element, just call the method directly on the selector, as I've done above.
First off, by using jQuery for what it's good at, you can replace this:
if ($('#Snowsports-row')[0].classList.contains("hidden") == false) {
$('#snowsports-only').removeClass("hidden")
}
with this:
$('#Snowsports-row').removeClass("hidden");
Your first block of code does the following:
With $('#Snowsports-row'), make a jQuery object that contains all DOM elements that match the select '#Snowsports-row'.
Then reach into the jQuery object with [0] and get the first DOM object in that jQuery object.
Then, use a property/method on that DOM element to determine if a class exists on that DOM element with your .classList.contains("hidden") reference.
Then, if you find that class, remove it.
A jQuery object contains inside it an array of DOM elements. If you call a method on the jQuery object itself like:
$('.tableRows').html("hello");
Then, you are asking jQuery to operate on ALL the DOM elements inside the jQuery object. You must use jQuery methods, not DOM methods.
If, on the other hand, you want to use a method such as .classList.contains(), that is only a method on an actual DOM element. That isn't a jQuery method. So, you have to reach inside of the jQuery object to get a specific DOM element out of it. That's what the [0] does. It reaches into the jQuery object and gets the first DOM element from its internal data structure. Once you have that DOM element, you can then use any DOM element methods on that DOM object.
FYI, if you ever want to get just the first DOM element from a jQuery object, but want the result to be a jQuery object, not just a DOM element, instead of [0], you can use .eq(0) like ths:
$('#Snowsports-row').eq(0).removeClass("hidden");
Now, in this specific case, this is never necessary because $('#Snowsports-row') cannot ever contain more than one DOM element because internally jQuery will only return the first matching DOM element when you are searching for a ID value (since there's never supposed to be more than one matching element with the same ID).
Just keep in mind that DOM element and a jQuery object are completely different types of objects with different methods on them. What makes it slightly confusing is that a jQuery object contains an internal list of DOM elements. But, if the object you are operating on is a jQuery object, then you can only call jQuery methods on it. If you reach into the jQuery object and pull out a DOM element, then you can only call DOM methods on it.
First of all, ids must be unique, so if you have more than one #Snowsports-only elements you can experience problems.
In your question, you are mixing jQuery code with pure Javascript code.
This:
if ($('#Snowsports-row')[0].classList.contains("hidden") {
...
}
Means that you get the first instance of #Snowsports-row (remember that is better if there is only one element with this id), but you get the DOM object (pure javascript) with the jQuery selector. You can do the same thing in jQuery like this:
$('#Snowsports-row').hasClass("hidden")
See more:
https://api.jquery.com/hasclass/
https://developer.mozilla.org/es/docs/Web/API/Element/classList
Sure, because you are operating over a list. Now, you're kind of mistaking the jQuery/javascript code. If you would like to use the same line twice you can basically drop jQuery altogether and write something like this:
var el = document.getElementById('Snowsports-row');
if (el.classList.contains('hidden')){
el.classList.remove('hidden');
}
In the first line you're selecting one specific DOM element, whereas in the second line you are selecting ALL elements in the DOM that fit that selector and removing the "hidden" class from all of them. Basically checking whether the element has a class can only be performed over an element (that's why you need to select the index, specifying a given element), but jQuery allows you to remove the class of every element inside a list (hence your second line)
Use jQuery's .eq() function. So:
var el = $('#Snowsports-row').eq(0);
if (el.hasClass("hidden")) {
$(el.removeClass("hidden")
}
There's also no harm in calling removeClass on an element that might not have that class... so:
$('#Snowsports-row').eq(0).removeClass('hidden');
I create an object in my javascript function, and I'd want to retrieve it in c++ from dom class for change some values, but I can access only by id, tag or class that are part of css syntax. Is there the possibility to get my object and set values or send to him those value?
First of all I think you should always get DOM elements by id or class name because IMHO it's the most versatile way to get things from this kind of tree.
Anyway, just like any other DOM Minko provides the childNodes and parentNode properties if you want/have to browse the tree :
AbstractDOM::childNodes()
AbstractDOM::parentNode()
When you've found the right DOM element, you can then use the other DOM methods to get its content, set its value, etc... Everything you need should be in the AbstractDOM base class definition.
I'm thinking it should return only exact match.
I see something like in source I have to work with:
var someArray = someObject.getElementsByTagName("item");
Except when I inspect the DOM, I don't see any tags named "items".
There are some css classes '.some_item_details'.
Subsequently I see an error about the element being null, which makes sense to me. What bugs me is that I'm seeing this in production codebase. So I'm thinking, "surely no one would commit something like this, I must be missing something".
Wouldn't 'item' have to exist a custom tag?!?
check getElementsByTagName()
Indeed,getElementsByTagName matches makes exact matches.
Returns an HTMLCollection of elements with the given tag name. The
complete document is searched, including the root node. The returned
HTMLCollection is live, meaning that it updates itself automatically
to stay in sync with the DOM tree without having to call
document.getElementsByTagName() again.
Note:
document.getElementsByTagName() is similar to
element.getElementsByTagName(), except that its search encompasses the
whole document.
#NiettheDarkAbsol's comment :
var someArray = someObject.getElementsByTagName("item");
Indeed, the code snippet you have shown would be searching for item
tags. This might be okay if someObject is an XML context.
Libraries I've seen have DOM wrappers that inclusively handle only the first element of the list in some case, like:
return this[0].innerHTML
and use the whole list in some other like:
for( var i=0, l=this.length; ++i<l; ) this[i].className = cls;
return this
Why is this approach accepted?
I think singling out the first element defeats the purpose of having methods that apply the same thing on the rest of the list. Isn't it bad to have dubious functions? I know it suits many people..but it feels inconsistent and I'm interested in why this is accepted so widely.
EDIT as an example:
jQuery.html()
If the selector expression matches more than one element, only the
first match will have its HTML content returned.
why not all?
the hide() method in bonzo, from Dustin Diaz
//...
hide: function () {
return this.each(function (el) {
el.style.display = 'none'
})
}
why not only the first?
The accessor methods in jQuery return single values because it's simpler and more generally useful. If the .html() API were to return the value if innerHTML for all elements, that'd mean it'd have to return an array. That, in turn, would mean that in the most common case of wanting the contents of a single element, you'd have to add the array access. There's also the problem of knowing exactly which returned value goes with which selected element. In other words, if .html() returned an array of element contents:
var contentList = $('.someClass, span, .hidden .container').html();
If "contentList" were just a simple array, what use would it be? How would the code know for each element which DOM node it came from? Of course there are solutions to this, but again the simple case is made complicated in order to support a rare general case.
It's possible of course to get the list yourself with .map(). I think this is just an issue of smart, practical, pragmatic API design.
I'm having trouble with jquery and selectors using the following code:
<div id="test"></div>
console.log($('#test'));
This always returns a list like [<div id="test"></div>] instead of the single element.
This results on always having to write $('#test')[0] for every operations instead of only $('#test'). Any idea on why?
Regards
Jquery will not return the HtmlElement, it returns a 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"
The jQuery object itself behaves much
like an array; it has a length
property and the elements in the
object can be accessed by their
numeric indices [0] to [length-1].
Note that a jQuery object is not
actually a Javascript Array object, so
it does not have all the methods of a
true Array object such as join().
http://api.jquery.com/Types/#jQuery
This is an example of the Composite Design Pattern
The Composite Pattern describes a group of objects that can be treated in the same way a single instance of an object can. Implementing this pattern allows you to treat both individual objects and compositions in a uniform manner. In jQuery, when we're accessing or performing actions on a single DOM element or a group of DOM elements, we can treat both in a uniform manner. http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#designpatternsjquery
jQuery encapsulates any objects found for a number of reasons. For one, it ensures that no matter what, null will never be returned for example. Rather, the elements found are inserted into a list. The fact that even though you're searching for a single element, the mechanism always remains the same and therefore the results will be placed into an array of one element rather than the element itself.
In jQuery it is better to think of selectors as matching multiple items, and your solution would be best if you use the each syntax to iterate through the matches items...
$('#test').each(function() {
console.log($(this));
});
As ID is not unique, jQuery looks for every element with such ID. So it's always returns a list, because threre is no guarantee that the element is exactly one