querySelectorAll not selecting all elements - javascript

if(document.readyState === 'complete') {
function $(elements) {
var matches = document.querySelectorAll(elements);
for (var i = 0; i < matches.length; i++) {
var item = matches[i];
}
return item;
}
}
$('div.test').style.fontSize = '36px';
<div class="test">asdf</div>
<div class="asdfd">test</div>
<div class="test">test</div>
I'd like to select all elements using querySelectorAll, but this seems to only affect the last element.

You are assigning the variable within the loop which will only return the last one. You should build an array of matches by declaring the variable outside of the loop or return the matches:
function $(elements) {
return document.querySelectorAll(elements);
}
Or:
function $(elements) {
var matches = document.querySelectorAll(elements);
var items = [];
for (var i = 0; i < matches.length; i++) {
items.push(matches[i]);
}
return items;
}

You assign each item to var item in turn.
After you've assigned the last one, you return the current value of item (which is the last one).
Return matches and then loop over it to set the font size of each item in turn.

Let's take a look at what your $ function is doing.
Select all items which match the query
Assign the first item in the list to item...
Assign the nth item to item
Return item which now contains the last element
So $() returns only the last element, and on that object, you are doing the assignment .style.fontSize = '36px'
There is no simple way to implement $ to do exactly what you are trying to. You could try a function which is called like this:
$(selector, {
fontSize : "36px"
});
It would look something like this:
function $(selector, properties) {
var matches = document.querySelectorAll(selector);
for (var i = 0; i < matches.length; i++) {
for (var j in properties) {
matches[i].style[j] = properties[j];
}
}
}
I'd recommend you fully understand what this is doing before moving on.
Also, the way you have used document.readyState makes it redundant. You should enclose the function call in your document.readyState, not the definition.

The variable item not is a array, then it is being overrided on each iteration loop.
Or define a array in order by save all selectors, or add the return in for loop.

Of course! the variable item holds the current iteration match. After the for cycle completes, it will naturally hold the last matched element. Javascript is executed sequentially, meaning the return statement will be executed after the forcycle.

I see you are trying to use chaining. This won't work with your current structure as your selector function will only ever return the last matched element from your querySlectorAll.
I think in this case it would be better to either pass a function that you want to do to each element or return an array/nodelist for another function to use;
function $(elements, method) {
var matches = document.querySelectorAll(elements);
for (var i = 0; i < matches.length; i++) {
method(matches[i]);
}
}
$('div.test', function (elem) {elem.style.fontSize = '36px';});

if(document.readyState === 'complete') {
function $(elements) {
var matches = document.querySelectorAll(elements);
for (var i = 0; i < matches.length; i++) {
var item = matches[i];
}
return item;
}
}
$('div.test').style.fontSize = '36px';
<div class="test">asdf</div>
<div class="asdfd">test</div>
<div class="test">test</div>

Related

How to write Javascript to search nodes - without getElementsByClassName

I'm very new at recursion, and have been tasked with writing getElementsByClassName in JavaScript without libraries or the DOM API.
There are two matching classes, one of which is in the body tag itself, the other is in a p tag.
The code I wrote isn't working, and there must be a better way to do this. Your insight would be greatly appreciated.
var elemByClass = function(className) {
var result = [];
var nodes = document.body; //<body> is a node w/className, it needs to check itself.
var childNodes = document.body.childNodes; //then there's a <p> w/className
var goFetchClass = function(nodes) {
for (var i = 0; i <= nodes; i++) { // check the parent
if (nodes.classList == className) {
result.push(i);
console.log(result);
}
for (var j = 0; j <= childNodes; j++) { // check the children
if (childNodes.classList == className) {
result.push(j);
console.log(result);
}
goFetchClass(nodes); // recursion for childNodes
}
goFetchClass(nodes); // recursion for nodes (body)
}
return result;
};
};
There are some errors, mostly logical, in your code, here's what it should have looked like
var elemByClass = function(className) {
var result = [];
var pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
(function goFetchClass(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ( pattern.test(nodes[i].className) ) {
result.push(nodes[i]);
}
goFetchClass(nodes[i].children);
}
})([document.body]);
return result;
};
Note the use of a regex instead of classList, as it makes no sense to use classList which is IE10+ to polyfill getElementsByClassName
Firstly, you'd start with the body, and check it's className property.
Then you'd get the children, not the childNodes as the latter includes text-nodes and comments, which can't have classes.
To recursively call the function, you'd pass the children in, and do the same with them, check for a class, get the children of the children, and call the function again, until there are no more children.
Here are some reasons:
goFetchClass needs an initial call after you've defined it - for example, you need a return goFetchClass(nodes) statement at the end of elemByClass function
the line for (var i = 0; i <= nodes; i++) { will not enter the for loop - did you mean i <= nodes.length ?
nodes.classList will return an array of classNames, so a direct equality such as nodes.classList == className will not work. A contains method is better.
Lastly, you may want to reconsider having 2 for loops for the parent and children. Why not have 1 for loop and then call goFetchClass on the children? such as, goFetchClass(nodes[i])?
Hope this helps.

Check inside a loop that array has next element in Javascript/QML

I have an array items. I need to make sure that in the current iteration of the loop I can safely call the next item of the array
for(var i = 0; i < items.length; ++i) {
// do some stuff with current index e.g....
item = items[i];
// then do something with item # i+1
if(items[i+1]) {
//do stuff
}
}
Is this how it is done or if not how/what would be the better way?
P.S. I do not want to do a bound check
If you want to loop through every element except the last one (which doesn't have an element after it), you should do as suggested:
for(var i = 0; i < items.length-1; ++i) {
// items[i+1] will always exist inside this loop
}
If, however, you want to loop through every element -even the last one-, and just add a condition if there is an element after, just move that same condition inside your loop:
for(var i = 0; i < items.length; ++i) {
// do stuff on every element
if(i < items.length-1){
// items[i+1] will always exist inside this condition
}
}
if(items[i+1]) will return false (and not execute the condition) if the next element contains a falsey value like false, 0, "" (an empty String returns false).
Put a value check on variable i and make sure it is less than items.length-1 in order to safely access items[i+1].
for(var i = 0; i < items.length-1; ++i) {
if(items[i+1]) {
//do stuff
}
}
Drop the for loop and use array#forEach, and simply check whether a next value exists:
items.forEach(function (item, index) {
if (!items[index + 1]) return;
// do something with item and items[index + 1]
});

removing element by tag name

I'm trying to remove an element by tag name using javascript. I set up a click handler for a button called "clear". I'm trying to use the function the function clear to remove all of the li elements from a list. This is what I have so far:
function clear() {
var list = document.getElementById("test").getElementsByTagName("li");
for (k = list.length; k >= 0; k++) {
var parent = list.parentNode;
parent.removeChild(list[k]);
}
}
Where "test" is the name of a ul element i have in the HTML. I'm getting a message in the console that parent is undefined. Any suggestions on how I need to modify the code so that I can delete the li elements?
The problem seems to be that you’re starting at list.length and incrementing while k >= 0. Infinite loop.
Apart from that, you need to use list[k].parentNode, and you’re not declaring k, so:
function clear() {
var list = document.getElementById("test").getElementsByTagName("li");
for (var k = list.length - 1; k >= 0; k--) {
var item = list[k];
item.parentNode.removeChild(item);
}
}
Almost there, two small errors :
function clear() {
var list = document.getElementById("test").getElementsByTagName("li");
for (var k = list.length; k-- > 0; ) { // decrement from list.length-1 to 0
var parent = list[k].parentNode; // you need the parent of the item, not the list
parent.removeChild(list[k]);
}
}
Note that decrementing instead of incrementing is the thing to do as the opposite order would make you skip some items of the dynamic nodelists.
You can generalize in somethig like that:
function clear(list) {
for (var i = list.length, li; li = list[--i];)
li.parentNode.removeChild(li);
}
clear(document.getElementById("test").getElementsByTagName("li"));
Your main errors was k++ instead of k-- and list.parentNode instead of list[k].parentNode – list is a NodeList, doesn't have a parent; each element of the list can have a different parent.
The NodeList returned by getElementsByTagName is (usually) live (dynamic). This means that it's length decreases as nodes are removed. You must therefore consider this in your loop.
Here are some more examples of how to get around it.
function clear() {
var nodes = document.getElementById("test").getElementsByTagName("li"),
i = nodes.length; // one var statement at beginning
// Choose one of
while (i--) { // same as `i-->0`
nodes[i].parentNode.removeChild(nodes[i]); // remove nodes from end
}
// OR
while (nodes.length) { // same as `nodes.length>0`, okay as dynamic list
nodes[0].parentNode.removeChild(nodes[0]); // remove nodes from start
}
// OR
for (; i >= 0; --i) {
nodes[i].parentNode.removeChild(nodes[i]); // remove nodes from end
}
// etc
}
HTML :
<ul id="test" type="bullet">
<li>apple</li>
<li>Orange</li>
</ul>
<button onclick="Clearall()">Click me</button>
If your UL tag only contains the LI tag then try like below... It will help you..
function Clearall()
{
document.getElementById("test").innerHTML="";
}
If your UL tag contains LI tag and other Tags then try like below.. It will help you..
Javascript :
function Clearall()
{
var ul = document.getElementById("test");
var lis = ul.getElementsByTagName("li");
while(lis.length > 0)
{
ul.removeChild(lis[lis.length - 1]);
}
}

use remove() on multiple elements

this: document.getElementsByClassName('warningmessage').remove(); doesn't work if you have multiple warningmessage elements on the page.
How can I just delete all elements with that class? do I have to use a for each? isn't there a command to deleteall()?
thanks for your tips!
With plain JavaScript you can do this:
var nodes = document.getElementsByClassName('warningmessage');
for(var i = 0; i < nodes.length; i++){
nodes[i].parentNode.removeChild(nodes[i]);
}
So you would first of all get the nodes you are interested in and afterwards iterate over them and remove them from their parents.
Sadly there is no forEach method on NodeList. However, you could this:
var nodes = document.getElementsByClassName('warningmessage');
[].forEach.call(nodes, function (node) {
node.parentNode.removeChild(node);
});
You need to use a loop. The below code shows how you write in "normal" javascript.
var elements = document.getElementsByClassName('warningmessage'),
element;
while (element = elements[0]) {
element.parentNode.removeChild(element);
}
The working demo.
​
This would be super easier using JQuery:
$('.warningmessage').remove();
But it's not that hard when you do it without. getElementsByClassName can return a nodelist of items. So you'll just have to loop through the list and remove each item:
var list = document.getElementsByClassName("warningmessage");
for(var i = list.length - 1; i >= 0; i--)
{
var n = list[i];
n.parentNode.removeChild(n);
}
You can try this
var elms= document.getElementsByClassName('warningmessage');
while(elms.length>0){
for(var i = 0; i < elms.length; i++){
elms[i].parentNode.removeChild(elms[i]);
}
}​
http://jsfiddle.net/gBwjA/
I have this problem before and I end up in this algorithm.
function removeElement(target) {
if(target.hasOwnProperty('length')) {
for(i=0; i<target.length; i++) {
removeElement(target[i]);
}
} else {
target.parentNode.removeChild(target);
}
}
and then you call the function like this:
removeElement(document.getElementById('the-id'));
or if you want to remove an HTML collection of elements then you call the function in this way:
removeElement(document.getElementsByTagName('tag-name'));

How do I make this loop all children recursively?

I have the following:
for (var i = 0; i < children.length; i++){
if(hasClass(children[i], "lbExclude")){
children[i].parentNode.removeChild(children[i]);
}
};
I would like it to loop through all children's children, etc (not just the top level). I found this line, which seems to do that:
for(var m = n.firstChild; m != null; m = m.nextSibling) {
But I'm unclear on how I refer to the current child if I make that switch? I would no longer have i to clarify the index position of the child. Any suggestions?
Thanks!
Update:
I'm now using the following, according to answer suggestions. Is this the correct / most efficient way of doing so?
function removeTest(child) {
if (hasClass(child, "lbExclude")) {
child.parentNode.removeChild(child);
}
}
function allDescendants(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
removeTest(child);
}
}
var children = temp.childNodes;
for (var i = 0; i < children.length; i++) {
allDescendants(children[i]);
};
function allDescendants (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
doSomethingToNode(child);
}
}
You loop over all the children, and for each element, you call the same function and have it loop over the children of that element.
Normally you'd have a function that could be called recursively on all nodes. It really depends on what you want to do to the children. If you simply want to gather all descendants, then element.getElementsByTagName may be a better option.
var all = node.getElementsByTagName('*');
for (var i = -1, l = all.length; ++i < l;) {
removeTest(all[i]);
}
There's no need for calling the 'allDescendants' method on all children, because the method itself already does that. So remove the last codeblock and I think that is a proper solution (á, not thé =])
function removeTest(child){
if(hasClass(child, "lbExclude")){
child.parentNode.removeChild(child);
}
}
function allDescendants (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
removeTest(child);
}
}
var children = allDescendants(temp);
You can use BFS to find all the elements.
function(element) {
// [].slice.call() - HTMLCollection to Array
var children = [].slice.call(element.children), found = 0;
while (children.length > found) {
children = children.concat([].slice.call(children[found].children));
found++;
}
return children;
};
This function returns all the children's children of the element.
The most clear-cut way to do it in modern browsers or with babel is this. Say you have an HTML node $node whose children you want to recurse over.
Array.prototype.forEach.call($node.querySelectorAll("*"), function(node) {
doSomethingWith(node);
});
The querySelectorAll('*') on any DOM node would give you all the child nodes of the element in a NodeList. NodeList is an array-like object, so you can use the Array.prototype.forEach.call to iterate over this list, processing each child one-by-one within the callback.
If you have jquery and you want to get all descendant elements you can use:
var all_children= $(parent_element).find('*');
Just be aware that all_children is an HTML collection and not an array. They behave similarly when you're just looping, but collection doesn't have a lot of the useful Array.prototype methods you might otherwise enjoy.
if items are being created in a loop you should leave a index via id="" data-name or some thing. You can then index them directly which will be faster for most functions such as (!-F). Works pretty well for 1024 bits x 100 items depending on what your doing.
if ( document.getElementById( cid ) ) {
return;
} else {
what you actually want
}
this will be faster in most cases once the items have already been loaded. only scrub the page on reload or secure domain transfers / logins / cors any else and your doing some thing twice.
If you use a js library it's as simple as this:
$('.lbExclude').remove();
Otherwise if you want to acquire all elements under a node you can collect them all natively:
var nodes = node.getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (hasClass(n, 'lbExclude')) {
node.parentNode.removeChild(node);
}
}
To get all descendants as an array, use this:
function getAllDescendants(node) {
var all = [];
getDescendants(node);
function getDescendants(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
getDescendants(child);
all.push(child);
}
}
return all;
}
TreeNode node = tv.SelectedNode;
while (node.Parent != null)
{
node = node.Parent;
}
CallRecursive(node);
private void CallRecursive(TreeNode treeNode)
{
foreach (TreeNode tn in treeNode.Nodes)
{
//Write whatever code here this function recursively loops through all nodes
CallRecursive(tn);
}
}

Categories

Resources