Javascript .removeChild() only deletes even nodes? - javascript

I am trying dynamically add children DIV under a DIV with ID="prnt". Addition of nodes work fine no problem. However strange enough when it comes to deleted nodes its only deleting the even numbered nodes including 0. Why is this, I could be something stupid but it seem more like a bug. I could be very wrong.
<script type="text/javascript">
function displayNodes()
{
var prnt = document.getElementById("prnt");
var chlds = prnt.childNodes;
var cont = document.getElementById("content");
for(i = 0; i < chlds.length; i++)
{
if(chlds[i].nodeType == 1)
{
cont.innerHTML +="<br />";
cont.innerHTML +="Node # " + (i+1);
cont.innerHTML +="<br />";
cont.innerHTML +=chlds[i].nodeName;
cont.innerHTML +="<br />";
}
}
}
function deleteENodes()
{
var prnt = document.getElementById("prnt");
var chlds = prnt.childNodes;
for(i = 0; i < chlds.length; i++)
{
if(!(chlds[i].nodeType == 3))
{
prnt.removeChild(chlds[i]);
}
}
}
function AddENodes()
{
var prnt = document.getElementById("prnt");
//Only even nodes are deletable PROBLEM
for(i = 0; i < 10; i++)
{
var newDIV = document.createElement('div');
newDIV.setAttribute("id", "c"+(i));
var text = document.createTextNode("New Inserted Child "+(i));
newDIV.appendChild(text);
prnt.appendChild(newDIV);
}
}
</script>
<title>Checking Div Nodes</title>
</head>
<body>
<div id="prnt">
Parent 1
</div>
<br />
<br />
<br />
<button type="button" onclick="displayNodes()">Show Node Info</button>
<button type="button" onclick="deleteENodes()">Remove All Element Nodes Under Parent 1</button>
<button type="button" onclick="AddENodes()">Add 5 New DIV Nodes</button>
<div id="content">
</div>
</body>

The problem is you're modifying the collection as you loop through it. Iterating in reverse should fix this...
for(i = chlds.length-1; i >= 0; i--)
{
if(!(chlds[i].nodeType == 3))
{
prnt.removeChild(chlds[i]);
}
}

Do away with the if(!(chlds[i].nodeType == 3)) nodeType check.
You can pull only the divs by doing a .querySelectorAll('div')
function deleteENodes()
{
var chlds = document.getElementById("prnt").querySelectorAll('div');<----
for(i = 0; i < chlds.length; i++)
{
if(!(chlds[i].nodeType == 3))
{
prnt.removeChild(chlds[i]);
}
}
}

The problem is as you're deleting nodes (starting from the beginning), the index of the remaining child nodes is decreasing by one each time:
Given 5 children:
Child1 Index0
Child2 Index1
Child3 Index2
Child4 Index3
Child5 Index4
When you go to remove them, this is what is happening
.removeChild(0) removes Child1
Child2 is now Index0
Child3 is now Index1
....
next Iteration:
.removeChild(1) removes Child3
Child2 is still Index0
Child4 is now Index1
Child5 is now Index2
....
Solution. Remove children from last index and move towards 0.

The NodeList returned by childNodes is "live" - removing a child updates the list immediately.
What's happening is that you're removing an element, which decreases the length of the list by 1, causing your loop to skip an element.
One way to avoid this is by copying the elements into anArray.
E.g.
var nodes = [];
for (var i = 0; i < chlds.length; i++) {
nodes.push(chlds[i]);
}
for(i = 0; i < nodes.length; i++)
{
if(!(nodes[i].nodeType == 3))
{
prnt.removeChild(nodes[i]);
}
}

Related

why `card` element is not duplicated 4 times

Why is card element not duplicated 4 times?
function generate4Cards() {
for (var i=0; i < 4; i++) {
var card = document.getElementById('card');
document.body.appendChild(card);
}
}
<body onload="generate4Cards()">
<div id="card">I am a card</div>
</body>
When you call appendChild with an existing element, that element will be removed from its former position in the DOM and appended to the new container. You'll have to explicitly create the div on each iteration instead.
But, having duplicate IDs in a single document is invalid HTML, so probably best to remove the id (or use a class instead):
for (var i = 0; i < 4; i++) {
var card = document.createElement('div');
card.textContent = 'I am a card';
document.body.appendChild(card);
}
Another option is to use cloneNode:
var originalCard = document.querySelector('div');
for (var i = 0; i < 4; i++) {
var card = originalCard.cloneNode(true);
document.body.appendChild(card);
}
<div>I am a card</div>
foo.append(bar) puts bar at the end of foo.
If I put my car at the end of my road, and then put my car at the end of my drive then I don't have two cars.
append only puts bar where you tell it to put it.
It doesn't duplicate it because it isn't supposed to.
Here is the sample code you are looking for -
<html>
<head>
<script>
function generate4Cards() {
for (var i = 0; i < 4; i++) {
var card = document.createElement('div');
card.textContent = 'I am a card';
document.getElementById('cardHolder').appendChild(card);
}
}
</script>
</head>
<body onload="generate4Cards()>
<div id="cardHolder"></div>
</body>
</html>

Searchbox in HTML and pure javascript to find all the matched anchor tags and display the results

HTML
I have a search box and some anchor tags. They can be random links on the page, but I have kept them inside divs to keep it simple. I am not sure how to loop through the elements and check if the link is available on the page.
function search() {
var i;
var div;
var query = document.getElementById('searchbox').value;
div = document.getElementsByTagName("div")[i];
var anchor = div.getElementsByTagName("a");
for (i = 0; anchor.length; i++) {
if (anchor.innerHTML.indexOf(query) > -1) {
document.getElementById('results').innerHTML = "results found";
}
}
}
<html>
<head></head>
<body>
<input type=text id="searchbox" placeholder=search onkeyup=search() autofocus>
<div><a href=#>Apple</a></div>
<br>
<div><a href=#>ball</a></div>
<br>
<div><a href=#>cat</a></div>
<div id=results></div>
<br>
</body>
</html>
I would also like to know how to display the results and the number of results...Thanks :)
placeholder=search onkeyup=search()
Should have double quotes around the values like so:
placeholder="search" onkeyup="search()"
For iterating your divs:
let count = 0; //variable for tracking count of links that match
let results = ""; //variable for displaying the results
var i;
var div;
var query = document.getElementById('searchbox').value;
//div = document.getElementsByTagName("div")[i]; //This is a no, only gets first div in collection
//change to this
div = document.getElementsByTagName("div");
//var anchor = div.getElementsByTagName("a"); //Move this inside your for loop
//Iterate your div elements
for (i = 0; i < div.length; i++) {
//Get any links of this div
const links = div[i].getElementsByTagName("a");
//Iterate the links
for(let j = 0; j < links.length; j++){
if (links[j].innerHTML.indexOf(query) > -1) {
//We found a match, add to count and append to string
count++;
results += results + "<br/>" + links[j].innerHTML;
}
}
}
//If we found at least one match
if(count > 0{
document.getElementById("results").innerHTML = "results found";
}else{
document.getElementById("results").innerHTML = "no results found";
}
//Populate the innerHTML of your elements to display the count (number of results)
//and the results
document.getElementById("yourResultCountElementId").innerHTML = count.toString();
document.getElementById("yourResultsElement").innerHTML = results;

Strange Javascript for loop behavior when using Jquery append

I'm probably missing/doing something silly, but I can't seem to work this out:
Here's a fiddle showing the problem:
https://jsfiddle.net/jhqjmcn4/1/
Note that you will need to open your console to see what is happening.
Basically, in this example, I have two functions containing a for loop that are identical to each other except the second one contains a JQuery append.
The goal of the function is to get the html elements from within a string, which works fine in the first function, but not the second.
As can be seen in the console, this causes anything that is not a text node to be ignored and not added to the list. In this case, the b and p tags are not being included.
Here is the code again:
JS:
function parse_text(text) {
var div = document.createElement("DIV");
div.innerHTML = text;
var elements = div.childNodes;
var container = jQuery("#container");
var list = [];
for (var i = 0; i < elements.length; i++){
var element = elements[i];
list.push(element);
console.log("First", list);
}
}
function parse_text_two(text) {
var div = document.createElement("DIV");
div.innerHTML = text;
var elements = div.childNodes;
var container = jQuery("#container2");
var list = [];
for (var p = 0; p < elements.length; p++){
var element = elements[p];
list.push(element);
console.log("Second", list);
container.append(element);
}
}
var text = "Here is <b>some</b> text with <p>html</p> in it";
parse_text(text);
parse_text_two(text);
html (irrelevant):
<div id="container">
</div>
<div id="container2">
</div>
Thanks in advance.
I suppose you need to have a Array.prototype.filter() method to get the html elements:
function parse_text_two(text) {
var div = document.createElement("DIV");
div.innerHTML = text;
var elements = [].filter.call(div.childNodes, function(el) {
return el.nodeType !== 3;
});
var container = jQuery("#container2");
var list = [];
for (var p = 0; p < elements.length; p++) {
var element = elements[p];
list.push(element);
console.log("Second", list);
container.append(element);
}
}
var text = "Here is <b>some</b> text with <p>html</p> in it";
parse_text_two(text);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
</div>
<div id="container2">
</div>
You can check updated fiddle here jsfiddle.net/bharatsing/jhqjmcn4/3/
Its return same result for both methods in console as per you want.
function parse_text_two(text) {
var div = document.createElement("DIV");
div.innerHTML = text;
var elementsOrg = div.childNodes;
var elements = Array.prototype.slice.call(elementsOrg);;
var container = jQuery("#container2");
var list = [];
for (var p = 0; p < elements.length; p++){
var element = elements[p];
list.push(element);
console.log("Second", list);
container.append(element);
}
}
The issue I saw by putting a debug point inside the loop was this:
The container.append(element); statement was actually modifying the elements array and was removing the appended element from the array. Which meant that in the loop for various values of 'p' the elements array looked like this:
p = 0 -> elements : [ text, b, text, p, text ] // text node at 0 picked
p = 1 -> elements : [ b, text, p, text ] // text node at 1 picked
p = 2 -> elements : [ text, p, text ] // text node at 2 picked
That is why the loop only ran 3 times instead of the original length of the elements array , i.e. 5.
This probably happens because jQuery 'moves' the node from its original place to the container div.
You can either clone the element node and then append into the container:
function parse_text_two(text) {
var div = document.createElement("DIV"),
p,
element,
elements,
container = jQuery("#container2"),
list = [];
div.innerHTML = text;
elements = div.childNodes;
for (p = 0; p < elements.length; p++){
element = elements[p];
list.push(element);
console.log("Second", list);
container.append($(element).clone());
}
}
Or use a while loop as suggested in Venkatesh's answer. Anyway, it is always better to know the root cause. :)
Hope this helps.
In second function in each loop comment this line container.append(element);

How can I set the innerText of two html nodes where one is a child?

In javaScript, can someone please help me to set the textContent of x amount of html nodes, where the html nodes can be child html nodes?
Here is the javaScript function that I have:
function setTextContent(className, data)
{
var elements = document.getElementsByClassName(className);
for (var i = 0, len = elements.length; i < len; i++) {
elements[i].textContent = data;
}
}
Here is the html that I have:
<div class="test"><div class="test2"></div></div>
Here are the two function calls:
setTextContent('test', "testString1");
setTextContent('test2', "testString2");
The data that I get displayed is just:
testString1
How can i get the following displayed:
testString1
testString2
I would like to please do this without jQuery and would like this code to be able to used on any element structure, where html nodes can have many other html nodes with html attributes.
Thanks.
From your example, looks like you want to insert the text at the beginning of the parent element so
function setTextContent(className, data) {
var elements = document.getElementsByClassName(className),
el;
for (var i = 0, len = elements.length; i < len; i++) {
el = elements[i];
if (!el.firstChild) { //if there are no contents within the element insert a text node
el.appendChild(document.createTextNode(''));
} else if (el.firstChild.nodeType != Node.TEXT_NODE) { //if the first child is not a text node then insert one
el.insertBefore(document.createTextNode(''), el.firstChild);
}
el.firstChild.textContent = data;
}
}
setTextContent('test', "testString1");
setTextContent('test2', "testString2");
<div class="test">
<div class="test2"></div>
</div>
function setTextContent(className, data) {
var elements = document.getElementsByClassName(className);
var node=document.createTextNode(data);
for (var i = 0, len = elements.length; i < len; i++) {
elements[i].insertBefore(node, elements[i].childNodes[0]);
}
}
setTextContent('test', "testString1");
setTextContent('test2', "testString2");
<div class="test">
<div class="test2"></div>
</div>
create text node and insert it before childNode[0]
Please try this code,
function setTextContent(className, data) {
var elements = document.getElementsByClassName(className);
for (var i = 0, len = elements.length; i < len; i++) {
elements[i].innerHTML = data + elements[i].innerHTML; //Append the data with children nodes.
}
}
Fiddle
Hope this will help you.
It seems you want to replace the first text node or nodes within an element. If there are no leading text nodes, insert some. The following should do the job:
function insertText(element, text) {
while (element.firstChild && element.firstChild.nodeType != 1) {
element.removeChild(element.firstChild);
}
element.insertBefore(document.createTextNode(text), element.firstChild);
}
<div id="d0">text at the begining<span> some in a span </span>and more<div>
<button onclick="insertText(document.getElementById('d0'), 'new text 0')">Insert 0</button>
<div id="d1">only text, nothing else<div>
<button onclick="insertText(document.getElementById('d1'), 'new text 1')">Insert 1</button>
<div>empty element following <span id="s0"></span><div>
<button onclick="insertText(document.getElementById('s0'), 'new text 2')">Insert 2</button>

Get index/position of element in div - pure javascript

How to get the position of last child in a element, I mean the number (the index)?
I want the code below to alert "4", since there is four elements.
<!DOCTYPE html>
<html>
<head>
<title>Untitled Document</title>
</head>
<body>
<div id="container">
<div id="div1">div1</div>
<div id="div2">div2
<div id="child_div1">div2</div>
<div id="child_div2">div2</div>
</div>
<div id="div3">div3</div>
<div id="div4">div4</div>
</div>
<script>
var container = document.getElementById('container'),
last_child_of_container = container.childNodes.length;
alert('position of last div is'+ last_child_of_container);
</script>
</body>
</html>
try
document.getElementById('container').children.length
http://jsfiddle.net/pxfunc/MGnCG/
This works :-)
var container = document.getElementById('container');
var count = 0;
var length = container.childNodes.length;
for (var i = 0; i < length; i++) {
if (container.childNodes[i].tagName &&
container.childNodes[i].tagName == 'DIV') {
count += 1;
}
}
alert(count);
Give document.getElementById('container').childElementCount a go
Here you go: http://jsfiddle.net/2t3VJ/
var container = document.getElementById('container'), children = 0;
for( var i=0, c = container.childNodes, l = c.length ; i < l; i++ ) {
if( c[i].nodeType == 1 ) { // if this is a HTMLSomeELement :)
children++;
}
}
alert( children );
Add h at the end of container.childNodes.lengt
container.childNodes.length;
See jsFiddle
For immediate child use childElementCount
container.childElementCount;
var container = window.document.getElementById("container");
var child_nodes = container.childNodes;
var child_nodes_length = child_nodes.length;
var count = 0;
var node;
var i;
for (i = 0; i < child_nodes_length; i++) {
node = child_nodes.item(i);
if (node.nodeType === 1) {
++count;
}
}
window.alert(count);
For people who wants any element (e.g. not necessarily the last one)
var wanted = document.getElementById('wanted');
var index = [].indexOf.call(wanted.parentElement.children, wanted);
Its very easy to get the position of the last element ... people are making it so complicated... try my code
function getLastElPos(container) {
return container.childNodes.length;
}
it returns the length of the child nodes in the container element, and the length means the last element (lol).
you can use it like this:
var container = document.getElementById('container');
var lastElement = getLastElPos( container );
alert( lastElement );
it works fine in all browser :)

Categories