Fallback is irrelevant. No libraries, please.
We have an dom object reference, we'll call obj. It's actually an event.target.
We have a node list, we'll call nodes, which we've gotten with querySelectorAll and a variable selector.
nodes may have 1 or many elements, and each each of those elements may have children.
We need to determine if obj is one of those node elements, or children elements of those node elements. We're looking for "native" browser functionality here, we can totes write our own for loop and accomplish this, we are looking for alternatives.
Something like:
nodes.contains(obj) OR nodes.indexof(obj)
Solutions involving other methods of retrieving the node list to match against are acceptable, but I have no idea what those could be.
If <=IE11 is not a concern then I think the cleanest is to use Array.from
Array.from(nodes).find(node => node.isEqualNode(nodeToFind));
I'm not sure if this will search beyond the first level of the NodeList, but you can use this expression recursively to traverse it and check if the element 'obj' is in the NodeList 'nodes'.
[].indexOf.call(nodes, obj)
I did something like this:
Array.prototype.find.call(style.childNodes, function(child) {
if(child.textContent.includes(drawer.id)) {
console.log(child);
}
});
Seems to work. Then child is another html node, which you can manipulate however you like.
I don't think there's a built-in DOM method for that. You'd need to recursively traverse your NodeList, and check for equality with your element. Another option is to use Element.querySelectorAll on each first-level elements from your NodeList (looking for your element's id, for example). I'm not sure how (inn)efficient that would be, though.
[...nodes].includes(targetNode)
In addition to Dominic's answer as function:
function nodelist_contains (nodelist, obj)
{
if (-1 < Array.from (nodelist).indexOf (obj))
return true;
return false;
}
Related
Is it possible to implement a search like document.querySelectorAll(selectors), but against an array of Node objects, rather than the entire document?
I have a pre-selected list of Node objects, and I want a function that could take the same selectors parameter, but search inside the array.
Basically, it is a question of whether or not it is possible to do a node.match(selectors) check, to verify if the node matches the selector, and then I could simply iterate through my list of nodes. But I haven't been able to find if such match-check is possible.
So I want something like this:
function querySelectorInArray(nodeArray, selectors) {
return nodeArray.filter(function(node) {
return node.match(selectors); // this line is what I want, but dunno how
});
}
Afterthought
Will it be a good idea though? I'm thinking, querySelectorAll is parsing selectors only once, but I would be doing it once for each element in the array. Will it be bad for performance? Or is there a way to avoid it, like maybe to pre-parse selectors and pass in an known object instead?
You can use Element#matches for that:
function querySelectorInArray(nodeArray, selectors) {
return nodeArray.filter(function(node) {
return node.matches(selectors);
});
}
You could simply add every node to a DocumentFragment and then use querySelector:
Documentation
I've read the following very good article:
Difference between Node object and Element object?
as to clear out the difference between a node object and an element object. I 've understood that
element objects are a subset of node objects.
So, after that I've come upon the following query: In which way may I iterate through all the node objects? By using document.getElementsByTagName('*') ,I think I am getting all the element objects since all of them have value 1 for their .nodeType property. Am I right and if yes, how may I include into my results all these nodes that are not elements?
Thank you
I don't believe there's any standard DOM function that will return every Node (as opposed to Element) in the entire document.
You would probably have to use recursive DOM traversal to find them all with something like this:
function getAllNodes(parent, nodes) {
parent = parent || document;
nodes = nodes || [];
var child = parent.firstChild;
while (child) {
nodes.push(child);
if (child.hasChildNodes) {
getAllNodes(child, nodes);
}
child = child.nextSibling;
}
return nodes;
}
As written you can just write var nodes = getAllNodes() and it'll automatically start at the document root.
The answer is in your question. You use document.getElementsByTagName. Let me repeat that for you. getElementsByTagName.
Non-element nodes don't have a tag name, an identifier, or anything like that. The only way to designate them is by where they are in the document structure.
You can get a reference to an Element and then browse its childNodes, as has been suggested already.
Another way is to use an XPath, which, unlike CSS selectors, is able to point directly to any Node, including text and comment Nodes.
document.evaluate("*", document.documentElement, null, XPathResult. UNORDERED_NODE_ITERATOR_TYPE)
Honestly, I don't know where to start with "extracting." My best guess would be to use the basic .html() tag from JQuery to solve this problem. This is for a small project to improve my JavaScript skills. Any ideas on how this could be done? Thanks so much...
I apologize for being unclear. I meant extracting all the links from a particular page from a domain I don't own. Then, putting these links into an array. Thanks!
Well, this comes to mind ?
var arr = [].slice.call( document.querySelectorAll('a') );
It gets all the matching elements with querySelectorAll, and converts the returned nodeList to an array using [].slice.call, where [] is a shortcut for Array.prototype. In other words it calls the native Array.slice method with call(), passing the elements in as the this value, effectively creating an array from the elements.
if you need the HTML, and not the DOM elements, you can map the elements array and return the outerHTML
var markup = arr.map(function(elem) { return elem.outerHTML; });
or if you just need the URL's, you can run the same map and return the href attribute instead
var urls = arr.map(function(elem) { return elem.getAttribute('href'); });
You don't necessarily need jQuery for this and if learning Javascript is your goal you might be better off without using it for now. querySelectorAll is available in all modern browsers and can accomplish what you are looking for. Per the documentation:
Returns a list of the elements within the document (using depth-first pre-order traversal of the document's nodes) that match the specified group of selectors. The object returned is a NodeList.
A NodeList is not an array however so you would need to do a little extra work to make an array of the link elements. You can read in the documentation why they are different.
The selectors used in the method are CSS Selectors and you can checkout the documentation for querySelector for examples.
So to do what you want you could do something like:
var a_list = document.querySelectorAll('a'); // returns NodeList
var a_array = Array.prototype.slice.call(a_list); // converts NodeList to Array
If the jQuery is an acceptable option, you can get all the links by a simple
var links = $('a[href]');
The "links" is an array already.
I would also fiddle around with something like this.
Array.prototype.slice.call(document.querySelectorAll('a'));
Googling it would be a good start so you can learn more about how it works and use it to your advantage.
What is Javascript alternative for this:
$('#clientDetailModal #heightValue')
I need it because in my code this works:
document.getElementById('heightValue').checkValidity()
but this doesn't:
$('#clientDetailModal #heightValue').checkValidity()
And I need to select only heightValue within clientDetailModal div.
Try $('#clientDetailModal #heightValue')[0].checkValidity()
The reason you need to do the [0] is, (as per the jquery id selector documentation)
Calling jQuery() (or $()) with an id selector as its argument will
return a jQuery object containing a collection of either zero or one
DOM element
Since you'll get a collection with 1 DOM element (assuming you don't have multiple ids), you need to then explicitly "select" that element using the [0].
You could use get to get the DOM element :
$('#clientDetailModal #heightValue').get(0).checkValidity()
Just to be sure, as your question might be a little ambiguous : only one element can have a given ID in HTML. So if your element is either absent or inside #clientDetailModal, then you could as well use
$('#heightValue').get(0).checkValidity()
It would also be faster. But in that case, there would be nothing wrong in using document.getElementById.
Since document.getElementById('heightValue').checkValidity() works, it means your function checkValidity() is attached on native DOM elements. This means, you can do:
$('#clientDetailModal #heightValue')[0].checkValidity()
Plus: If your HTML is valid with no duplicate IDs, you can simply do
$('#heightValue')[0].checkValidity()
Since the OP asked for a JavaScript alternative. On modern browsers,
document.querySelector ('#clientDetailModal #heightValue')
will return the element you are asking for.
The direct equivalent would be
document.querySelectorAll ('#clientDetailModal #heightValue')
which returns an array of elements matching the selector requested, do yrou will need to add the [0] as per the other answers.
I presume this is what you're looking for :
document.getElementById('clientDetailModal').getElementById('heightValue').checkValidity();
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.