how to avoid "undefined" inside a "for-loop" in javascript? - javascript

I'm changing the innerHTML with a for loop, but some values returns undefined since there's not the same amount of instances. How can I set up the loop to skip the empty instances?
This is to change the content of all instances with the same class, the amount of instances vary upon the class.
<!DOCTYPE html>
<html>
<body>
<p class="TEXT1">000</p>
<p class="TEXT1">000</p>
<p class="TEXT2">000</p>
<p class="TEXT3">000</p>
<p class="TEXT3">000</p>
<p class="TEXT3">000</p>
<p class="TEXT4">000</p>
<p class="TEXT5">000</p>
<p class="TEXT5">000</p>
<p class="TEXT5">000</p>
<p class="TEXT5">000</p>
<script>
function CHANGE() {
for (var i = 0; i < 10; i++) {
//"paragraphs" will be different content texts, not just the word with a number.
document.getElementsByClassName("TEXT1")[i].innerHTML = "Paragraph 1";
document.getElementsByClassName("TEXT2")[i].innerHTML = "Paragraph 2";
document.getElementsByClassName("TEXT3")[i].innerHTML = "Paragraph 3";
document.getElementsByClassName("TEXT4")[i].innerHTML = "Paragraph 4";
document.getElementsByClassName("TEXT5")[i].innerHTML = "Paragraph 5";
}
}
CHANGE();
</script>
</body>
</html>
The expected result is to get:
Paragraph 1
Paragraph 1
Paragraph 2
Paragraph 3
Paragraph 3
Paragraph 3
Paragraph 4
Paragraph 5
Paragraph 5
Paragraph 5
Paragraph 5
The actual result I get is:
Paragraph 1
Paragraph 1
Paragraph 2
Paragraph 3
000
000
Paragraph 4
Paragraph 5
000
000
000
Javascript Console:
Uncaught TypeError: Cannot set property 'innerHTML' of undefined

the amount of instances vary upon the class.
You could check the number of instances.
for (var el of document.getElementsByClassName("TEXT1"))
el.innerHTML = "Paragraph 1";
For multiple classes and texts:
let change = (cls, text) => {
for (var el of document.getElementsByClassName(cls))
el.innerHTML = text;
};
change('text1', 'paragraph 1');
change('text2', 'paragraph 2');
...

Inside your for loop check this condition
for(// your loop condition){
myVar = document.getElementsByClassName("TEXT")[i];
if (typeof myVar !== 'undefined'){
continue;
}
}

You shouldn't be trying to change all of the different categories of elements in one loop, nor should you be hardcoding the length of the loop or re-calculating the list of elements in every loop. Try this:
var text1Elements = document.getElementsByClassName("TEXT1");
for (var i = 0; i < test1Elements.length; i++) {
text1Elements[i].innerHTML = "Paragraph 1";
}
Then repeat that for every different class of element you want to manipulate. Alternatively, give all these elements an extra, common class, and just find and loop through the results for that, using a regular expression to determine the number. I'll include this example using ES6 syntax, since it's more complex anyway:
// Each element has the 'paragraph' class; for instance, class="TEXT1 paragraph", class="TEXT2 paragraph", etc.
const elements = document.querySelectorAll('.paragraph');
for (let element of elements) {
const matcher = element.className.match(/^TEXT(\d+)/i);
element.innerHTML = `Paragraph ${matcher[1]}`;
}

Every time you call document.getElementsByClassName, you are forcing the DOM to find and collect those elements. This makes for a slow web site. try something like this:
for (var i=1; i < 6; i++) {
var elements = document.getElementsByClassName("TEXT" + i);
for (var element of elements) {
element.innerHTML = "Paragraph " + i;
}
}

You are trying to iterate over indexes that are not present try this snippet:
function change() {
var classes = ['TEXT1', 'TEXT2', 'TEXT3', 'TEXT4', 'TEXT5'];
var paragraphs = ['Paragraph 1', 'Paragraph 2', 'Paragraph 3', 'Paragraph 4', 'Paragraph 5', ];
for (var i = 0; i < classes.length; i++) {
var elements = document.getElementsByClassName(classes[i]);
for (var j = 0; j < elements.length; j++) {
elements[j].innerHTML = paragraphs[i];
}
}
}
window.onload = function() {
change();
}
<!DOCTYPE html>
<html>
<body>
<p class="TEXT1">000</p>
<p class="TEXT1">000</p>
<p class="TEXT2">000</p>
<p class="TEXT3">000</p>
<p class="TEXT3">000</p>
<p class="TEXT3">000</p>
<p class="TEXT4">000</p>
<p class="TEXT5">000</p>
<p class="TEXT5">000</p>
<p class="TEXT5">000</p>
<p class="TEXT5">000</p>
</body>
</html>

You should instead start with the classNames and search for matching elements. Once you have these you can operate on them
function CHANGE() {
// define the classnames for TEXT1, TEXT2, ... TEXT6
for(let i=1;i<=6;i++){
let className = "TEXT"+i
// pickup the child elements with the matching class
let nodes = document.getElementsByClassName(className)
// change the text
for(let j=0; j<nodes.length;j++){
nodes[j].innerHTML="Paragraph "+i
}
}
}
Alternatively if you just wanted to work from a list of classnames you could say
function CHANGE() {
const classNames = ['TEXT1','TEXT2','TEXT3','TEXT4','TEXT5','TEXT6']
classNames.forEach(name=>{
let nodes = document.getElementsByClassName(name)
let innerText = "Paragraph "+name.replace("TEXT","")
for(let j=0; j<nodes.length;j++){
nodes[j].innerHTML=innerText
}
})
}

Related

How to get the text from a div which has children in it

I am currently studying JavaScript and I have the following problem. Is it possible to get only the text from a div which has children inside it? I managed to make it work only for the text which appears before the div's children.
PS: I would like to mention that I am trying to achieve this using only pure JavaScript.
var Class = document.querySelectorAll('div,b');
for (var i=0; i < Class.length; i++){
console.log(Class[i].childNodes[0].nodeValue);
}
<div class="Bio">
My name is <b>John Doe</b> and I am coming from Texas
</div>
<div class="Bio">
My name is <b>Jean Frye</b> and I am coming from Alabama
</div>
It's not very clean way, but try something like this :
//get all div's with Bio css class (You can change it)
var Class = document.querySelectorAll('.Bio');
var sp=document.getElementById('res');
var arr=[]; //I put result into array so You can use it where You need
for (var i=0; i < Class.length; i++) {
for(var x=0;x<Class[i].childNodes.length;x++) {
if(Class[i].childNodes[x].nodeValue==null) {
//get value, innerHTML, from <b>
//res.innerHTML+=Class[i].childNodes[x].innerHTML+'<br>';
arr.push(Class[i].childNodes[x].innerHTML);
} else {
//get div innerHTML (before,after every child node
//res.innerHTML+=Class[i].childNodes[x].nodeValue+'<br>';
arr.push(Class[i].childNodes[x].nodeValue);
}
}
}
//show result into that span
for(var i=0;i<arr.length;i++) {
res.innerHTML+=arr[i]+'<br>';
}
<div class="Bio">
My name is <b>John Doe</b> and I am coming from Texas
</div>
<div class="Bio">
My name is <b>Jean Frye</b> and I am coming from Alabama
</div>
<br><br>
<!-- I use this span to show result -->
<span id="res"></span>
var Class = document.querySelectorAll('div');
for (var i=0; i < Class.length; i++){
var children = [];
var boldText = Class[i].querySelectorAll('b')[0].innerText;
var otherText = Class[i].innerText.split(Class[i].querySelectorAll('b')[0].innerText)
children.push(otherText[0]);
children.push(boldText);
children.push(otherText[1]);
console.log(children);
}
Output :-
["My name is ", "John Doe", " and I am coming from Texas"]
["My name is ", "Jean Frye", " and I am coming from Alabama"]
This might do the trick.
You can use innerText to get only the text of your selected element.
var Class = document.querySelectorAll('div');
for (var i=0; i < Class.length; i++){
console.log(Class[i].innerText);
}
<div class="Bio">
My name is <b>John Doe</b> and I am coming from Texas
</div>
<div class="Bio">
My name is <b>Jean Frye</b> and I am coming from Alabama
</div>
For more information, reference the MDN article on innerText

Does not append elements to the first tag

function $(selector) {
var resultObject = {
append: function (element) {
var parser = new DOMParser();
var dos = parser.parseFromString(element, "text/html");
var all = dos.getElementsByTagName("body")[0];
var elemWhichAppend = document.getElementsByTagName(selector);
var children = all.childNodes;
for (var i = elemWhichAppend.length-1; i >=0; i--) {
var msgContainer = document.createDocumentFragment();
var children = all.childNodes;
for (var child = 0; child < children.length; child++) {
var al = children[child];
msgContainer.appendChild(al);
}
insertAfter(msgContainer, elemWhichAppend[i]);
}
}
}
return resultObject;
}
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="jqueryjs.js"></script>
</head>
<body>
<h1 class="testing">APPEND</h1>
<p>Hallo, ich bin ein P TAG </p>
<h1 class="testing">APPEND2</h1>
<p>BADGL</p>
<input type="button" value="append tag/text " onclick="$('p').append('<ul><li>RIBA RIBI<ul><li>FRANK RIBERY</li></ul></li></ul> <h1>NIGOGOG</h1> messi ist scheiße');" />
</body>
</html>
Run code snipped. As you can see the function adds only to the last p element the tags. And forgets the 'messi ist scheiße'. I tried a lot of thinks but I am not able to find my bug. Is it because the 'messi ist scheiße' a text is?
You made this way more complicated than it needs to be. Just make the append function append your string onto the element's innerHTML.
function $(selector) {
var resultObject = {
append: function (element) {
var elemWhichAppend = document.getElementsByTagName(selector);
for (var i = elemWhichAppend.length-1; i >=0; i--) {
elemWhichAppend[i].innerHTML += element;
}
}
}
return resultObject;
}
<h1 class="testing">APPEND</h1>
<p>Hallo, ich bin ein P TAG </p>
<h1 class="testing">APPEND2</h1>
<p>BADGL</p>
<input type="button" value="append tag/text " onclick="$('p').append('<ul><li>RIBA RIBI<ul><li>FRANK RIBERY</li></ul></li></ul> <h1>NIGOGOG</h1> messi ist scheiße');" />
Wrapping (messi ist scheiße) in a div, p or h1 tag will do the trick.
<input type="button" value="append tag/text " onclick="$('p').append('<ul><li>RIBA RIBI<ul><li>FRANK RIBERY</li></ul></li></ul> <h1>NIGOGOG</h1> <p>messi ist scheiße</p>');" />
On testing this a little in JS fiddle, i found that as you loop through the child elements, for some reason the variable you declared as 'all' seems to be dissappearing, or at least deteriorating in nature to the extent that it doesn't contain any child elements on the second loop, and so does not go through the Child-elements loop the second time around.
One thing that seemed to improve performance was redefining all and dos at the top of the loop:
for (var i = elemWhichAppend.length-1; i >=0; i--) {
var dos = parser.parseFromString(element, "text/html");
var all = dos.getElementsByTagName("body")[0];
var msgContainer = document.createDocumentFragment();
...
This should help, as it certainly solved the JSFiddle I was on. I'm not certain why the 'all' variable is changing in nature exactly, but it changes - as i saw from the log - as soon as you perform this line:
msgContainer.appendChild(al);
I imagine it happens after you perform that line because that line actually accesses the 'document' object and causes it to change (since msgContainer is derived from document object).
Also, you would probably have to include the 'messi ist Shiese' into a html tag, as the line
var dos = parser.parseFromString(element, "text/html");
This line seems to only find the HTML elements in the element you've provided. You can check this by trying to console.log the 'child' element itself, and you will see that when you include the 'messi ist sheise' part into a tag, it will appear, but it won't appear when it's not in a tag.
for (var child = 0; child < children.length; child++) {
var al = children[child];
console.log(children[child]);
msgContainer.appendChild(al);
}
You've missed a "" near getElementsByTagName

innerHTML in DOM

I am unable to change the text inside my 'p' tag using this script
<script>
var firstItem = document.getElementByTagName('p');
firstItem.innerHTML = 'Adding Javascript';
</script>
You have several coding errors. Here's some corrected code:
<script>
var firstItem = document.getElementsByTagName('p')[0];
firstItem.innerHTML = 'Adding Javascript';
</script>
The correct method is document.getElementsByTagName('p'). Note the "s" at the end of "Elements".
Then, because document.getElementsByTagName('p') returns an HTML collection object, you have to either iterate over the collection or reach into the collection to grab a specific DOM object (which I did in my example with [0]).
And here's a working code snippet:
// change just the first <p> tag
document.getElementById("test1").addEventListener("click", function(e) {
var firstItem = document.getElementsByTagName('p')[0];
firstItem.innerHTML = 'Adding Javascript';
});
// change all the <p> tags
document.getElementById("test2").addEventListener("click", function(e) {
var items = document.getElementsByTagName('p');
for (var i = 0; i < items.length; i++) {
items[i].innerHTML = 'Setting All Items';
}
});
<button id="test1">Change text of first item</button><br><br>
<button id="test2">Change text of all items</button><br><br>
<p>This is some text</p>
<p>This is more text</p>
<p>This is and more text</p>

How could I add the same string on different paragraph multiple time on the same HTML page?

I wish to know the best way to write only once the same thing and repeat inside the same page. For example:
<div>
<p id="description1"></p>
</div>
<div>
<p id="description1"></p>
</div>
--
I wish to write only one time the description1 inside the body. I think this could be achieved using the DOM.
Put the elements in the same class using the class attribute, then get the list of all elements using the getElementsByClassName() DOM function. You can then go over the list using a for loop.
[].forEach.call(document.getElementsByClassName("description"), function(elem) {
elem.innerHTML = "StackOverflow saved my day!";
});
You can even put the text in all elements of the same class using no JavaScript and only CSS by using the content attribute.
First of all, the ID field should be unique per element.
If you give all the tags a class <p class="description"></p> then you can use jQuery to set them all by calling:
$('.description').text('This is the text')
In javascript:
var elements = document.getElementsByClassName("description");
for (var i = 0; i < elements.length; i++) {
elements[i].innerHTML = "This is the text.";
}
Have a look at the solutions proposed here
How to repeat div using jQuery or JavaScript?
this one seems to work pretty well:
html:
<div id="container">data</div>
js:
var container = document.getElementById('container');
function block(mClass, html) {
//extra html you want to store.
return '<div class="' + mClass + '">' + html + '</div>';
}
// code that loops and makes the blocks.
// first part: creates var i
// second: condition, if 'i' is still smaller than three, then loop.
// third part: increment i by 1;
for (var i = 0; i < 3; i++) {
// append the result of function 'block()' to the innerHTML
// of the container.
container.innerHTML += block('block', 'data');
}
JSFIDDLE
Just added with a code by using
getElementsByClassName()
`<html>
<body>
<div class="example">First div element with class="example".</div>
<p class="example">Second paragraph element with class="example".</p>
<p>Click the button to change the text of the first div element with class="example" (index 0).</p>
<button onclick="myFunction()">Try it</button>
<p><strong>Note:</strong> The getElementsByClassName() method is not supported in Internet Explorer 8 and earlier versions.</p>
<script>
function myFunction() {
var x = document.getElementsByClassName("example");
for(var i=0;i< x.length;i++)
x[i].innerHTML = "Hello World!";
}
</script>
</body>
</html>`
If you wish to keep id, change your code like this :
script :
var pcount = 2// # p
var desc = document.getElementById('description1');
for(i=0; i<pcount;i++){
document.getElementById('description' + i).innerHTML = desc;
}
html
<div>
<p id="description1"></p>
</div>
<div>
<p id="description2"></p>
</div>
two elements cannot have same id but can have same class
<head>
<script>
var x = document.getElementsByClassName("description");
for (var i = 0; i < x.length; i++) {
x[i].innerHTML = "This is the text.";
}
</script>
<style>
.description1 { // this will apply the same style to all elements having class as description1
text-align: center;
color: red;
}
</style>
</head>
<body>
<div>
<p class="description1"></p>
</div>
<div>
<p class="description1"></p>
</div>
</body>
See the script tag. this solves your problem

How to get innerHTML of DIV without few inside DIV's?

I have some DIV, what contains HTML with images, styles e.t.c. I want to remove exact div's that contains id = 'quot' or className = 'quote', but i don't understand how i can get not only innerHTML of each tag. For example, < p > and < /p > which don't have innerHTML also should be included in final parsed HTML.
var bodytext = document.getElementById("div_text");
var NewText = "";
if (bodytext.hasChildNodes){
var children = bodytext.childNodes;
for (var i = 0; i < children.length; i++){
if (children[i].id != "quot" && children[i].className != "quote" && children[i].innerText != ""){
NewText = NewText + children[i].innerHTML;
}
}
HTML of source need to be parsed:
<div id="div_text">
<p>
Some Text</p>
<p>
Some Text</p>
<p>
<img alt="" src="localhost/i/1.png" /></p>
<div id="quot" class="quote" />
any text <div>text of inside div</div>
<table><tr><td>there can be table</td></tr></table>
</div>
<p>
</p>
</div>
Desired output:
<p>
Some Text</p>
<p>
Some Text</p>
<p>
<img alt="" src="localhost/i/1.png" /></p>
<p>
</p>
Just grab a reference to the targeted divs and remove them from their respective parents.
Perhaps something a little like this?
EDIT: Added code to perform operation on a clone, rather than the document itself.
div elements don't have .getElementById method, so we search for an element manually.
window.addEventListener('load', myInit, false);
function removeFromDocument()
{
// 1. take car of the element with id='quot'
var tgt = document.getElementById('quot');
var parentNode = tgt.parentNode;
parentNode.removeChild(tgt);
// 2. take care of elements whose class == 'quote'
var tgtList = document.getElementsByClassName('quote');
var i, n = tgtList.length;
for (i=0; i<n; i++)
{
// we really should be checking to ensure that there aren't nested instances of matching divs
// The following would present a problem - <div class='quote'>outer<div class='quote'>inner</div></div>
// since the first iteration of the loop would also remove the second element in the target list,
parentNode = tgtList[i].parentNode;
parentNode.removeChild(tgtList[i]);
}
// 3. remove the containing div
var container = document.getElementById('div_text');
container.outerHTML = container.innerHTML;
}
function cloneAndProcess()
{
var clonedCopy = document.getElementById('div_text').cloneNode(true);
var tgt;// = clonedCopy.getElementById('quot');
var i, n = clonedCopy.childNodes.length;
for (i=0; i<n; i++)
{
if (clonedCopy.childNodes[i].id == 'quot')
{
tgt = clonedCopy.childNodes[i];
var parentNode = tgt.parentNode;
parentNode.removeChild(tgt);
break; // done with for loop - can only have 1 element with any given id
}
}
// 2. take care of elements whose class == 'quote'
var tgtList = clonedCopy.getElementsByClassName('quote');
var i, n = tgtList.length;
for (i=0; i<n; i++)
{
// we really should be checking to ensure that there aren't nested instances of matching divs
// The following would present a problem - <div class='quote'>outer<div class='quote'>inner</div></div>
// since the first iteration of the loop would also remove the second element in the target list,
parentNode = tgtList[i].parentNode;
parentNode.removeChild(tgtList[i]);
}
// 3. remove the containing div
//var container = clonedCopy; //.getElementById('div_text');
//container.outerHTML = container.innerHTML;
console.log(clonedCopy.innerHTML);
}
function myInit()
{
cloneAndProcess();
//removeFromDocument();
}

Categories

Resources