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
Related
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.
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.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Javascript custom index accessor
If I were to write, say, a doubly-linked list object in Javascript, is there a way to reference this new list in a fashion like an array?
For instance, if I wanted the value of node 5, I'd like to query
newLinkedList[5]
instead of doing what I've been doing, which is something like
newLinkedList.getNode(5)
Basically, is there a way to "pretty up" references to custom data structures, or do you have to do it as a custom function each time?
Arrays in Javascript are really just objects with special handling for numerical properties. You can accomplish the same thing yourself, but currently Javascript doesn't provide a simple mechanism for transparent two-way accessing of those indices; you'll have to handle adding and deleting objects with named methods, but you can still benefit from reading them with array-like indexing.
Here's an example of a simple custom "Arrayish" object:
var ll_array = {};
ll_array.length = 0;
ll_array.addNode = function (newNode) {
this[this.length] = newNode;
this.length++;
};
ll_array.addNode('Foo');
ll_array.addNode('Bar');
console.log('Length: ' + ll_array.length);
console.log(ll_array);
console.log(ll_array[0]);
http://jsfiddle.net/rbmsJ/1/
The brute force method of traversing the list, then assigning each item in the traversal to an array element, is the first thing that comes to mind:
Pretend you have a LinkedList object. Let's also presume there are methods for iterating the list called Head, and Next. Now, caveat being that this is entirely untested, you could create a prototype toArray() method in a manner such as:
LinkedList.prototype.toArray() = function () { var array={};
var currentItem = list.head;
while (currentItem!=null){
array.addNode(currentItem);
currentItem=currentItem.Next;
}
return array;
}
Obviously this must allow for other presumed functions, but the idea is to traverse the linked list, and put a reference to each element in an array, and return it. Hope that helps in some way.
From your question:
objects are saved in a double-linked list.
you know the getNode(int n) method to get n-th element.
there are no way to allow you get the n-th element with array style, [n]. because linked lists are not arrays, the elements in a linked list are linked to each other, this data structure is different from ordinary array or JavaScript style array:named property.
Arrays ARE double linked lists without the links. To traverse right you simply increment the index, to traverse left you decrement the index.
You could write yourself a set of functions to perform the basic list operations and still have the facility of using simple indexing to reach any aribtrary element.
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;
}
Is there a way in java script to get only a particular name instead of using document.getElementsByName("x"); which return an array? I have a kind of special situation where i can’t use the id. Any suggestions please?
Thank You.
Just get the first element:
document.getElementsByName("x")[0];
Or for safety:
function getFirstElementByName(element_name) {
var elements = document.getElementsByName(element_name);
if (elements.length) {
return elements[0];
} else {
return undefined;
}
}
(BTW getElementsByName returns a collection, not an array.)
If you're looking for a single element, take the first one from the nodelist, for example:
var element = document.getElementsByName("x")[0];
You can test it out here.
Or use jQuery, so you don't have to bother with all the browser annoyance.
You just have to do this:
$("*[name='x']").first();
To get the first element with that name. If you know the element type than you can use it instead of "*". jQuery will make your life easier every time!