removing element by tag name - javascript

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]);
}
}

Related

querySelectorAll not selecting all elements

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>

For loop on an object is getting out of control

I'm working with a project that has this crazy for loop to expand nodes in a D3.js canvas interactive. Essentially, what I want is to expand all children. So if an object has a child, I want to expand them.
I cut out a chunk of code from this. There's so many for loops it's ridiculous. How can I reduce this to a simple "find all children, preform toggle(); and update();"?
$('.expandAll').click(function(e) {
e.preventDefault();
length = root.children.length;
for (var i = 0; i < length; i++) {
toggle(root.children[i]);
update(root);
if (root.children[i]['children']) {
childlength = root.children[i]['children'].length;
for (var j = 0; j < childlength; j++) {
toggle(root.children[i]['children'][j]);
update(root);
if (root.children[i]['children'][j]['children']) {
childlength2 = root.children[i]['children'][j]['children'].length;
for (var k = 0; k < childlength2; k++) {
toggle(root.children[i]['children'][j]['children'][k]);
update(root);
}
}
}
}
}
});
Sounds like a good case for recursion:
$('.expandAll').click(function(e) {
e.preventDefault();
expandAll(root);
});
var expandAll = function (node) {
toggle(node);
update(node);
// edit: if nodes with no children are lacking the children property
if (!node.children) {
return;
}
for (var i = 0, length = node.children.length; i < length; i++) {
expandAll(node.children[i]);
}
};
I'm not sure what toggle and update exactly means, but you may be able to perform just a single top-level update call after calling expandAll(root);.
Not sure if this will help, but something I've been doing with nested objects:
object = {
children: [], // array of similar objects with children property and update function
update: function(data){
// update this item
this.data = data;
// update each immediate child
if (this.children)
for (var i in this.children)
this.children[i].update(data);
}
};
// then you just call update on the first item:
object.update(data);
If you follow this pattern, rather than setting up complex loops at the root level, you simply loop through the immediate children and call their update function, which then loops through their children, and goes all the way down.
I'm not a great JS developer, just something I was doing for some nested comments I was working with the other day ;)
Use recursion! If you need to support only three levels, you can introduce a counter variable for that.
$('.expandAll').click(function(e) {
e.preventDefault();
expandAll(root, root.children/*, 3*/);
}
function expandAll(root, children/*, lev*/) {
if (!children/* || lev<=0 */) return;
var length = children.length;
for (var i = 0; i < length; i++) {
toggle(children[i]);
update(root);
expandAll(root, children[i].children/*, lev-1*/);
}
}
Btw, are you sure that you need to call update on the root after every single toggle? It would make more sense to me to call it once in the end when all children are toggled.

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'));

Need help finding/traversing dom in Javascript

I'm sure this is a redundant question, but I've looked for an hour or so and come up empty-handed so was hoping someone could help...
Looking for a way to use JS (not jquery) to return the class of the li below when searching for 'Chicken' (or whatever the value is).
<li class='113252'>
<span>Chicken</span>
</li>
So was hoping the javascript would return the li class when given the span value (in this case Chicken).
Thanks!
Try this:
var spanArray = document.getElementsByTagName('span');
for (var i=0; i<spanArray.length; i++) {
if(spanArray[i].innerHTML.toUpperCase() === 'CHICKEN')
{
alert(spanArray[i].parentNode.className);
break;
}
}
Now, I'm more familiar with jQuery but seems to work in the fiddle linked here: http://jsfiddle.net/FranWahl/fCzYc/2/ (Updated to include suggested break; after match)
You can add more type checking for the parentNode to ensure it is an li and so on, but this should get you started.
Also, I'm not sure at all how efficient this is in a big document.
Edit
Having read through some comments I have updated my code above to include the break as suggested by ajax333221.
Dennis mentioned that it would be better to call getElementByTagName on the ul.
Given you can have an li without a ul I added it here as separate code as I'm not sure if the OP has ul tags.
Code querying against each ul (jsFiddle here)
var ulArray = document.getElementsByTagName('ul');
var parentFound = false;
for (var i = 0; i < ulArray.length; i++) {
var spanArray = ulArray[i].getElementsByTagName('span');
for (var i = 0; i < spanArray.length; i++) {
if (spanArray[i].innerHTML.toUpperCase() === 'CHICKEN') {
alert(spanArray[i].parentNode.className);
parentFound = true;
break;
}
}
if(parentFound)
{
break;
}
}​
This is by no means fully complete, which is why you should seek a library, but it does two things:
recursively traverse child elements (starting with the document's BODY), to find the supplied text
recursively traverse the parent element to find the supplied parent element tag, once found it will return the class of that parent
JSFiddle: http://jsfiddle.net/vol7ron/bttQN/
function getParentClass(element, parentTag){
if (element.tagName == parentTag.toUpperCase())
return element.className;
return getParentClass(element.parentNode,parentTag);
}
window.findParentClass = function (text,tagName){
var elements = document.getElementsByTagName('body');
for (var n=elements.length; n--;){
var foundClass = (function searchNextChild(el){
if (!el.children.length) {
if (el.textContent == text)
return getParentClass(el,tagName);
return;
}
for (var i=0, n=el.children.length; i<n; i++)
return searchNextChild(el.children[i]);
})(elements[n]);
return foundClass;
}
};
Example Call:
alert( findParentClass('Chicken','li') );

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