getElementsByClassName produces error "undefined" [duplicate] - javascript

This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 8 years ago.
I have several textboxes with the class output. I would like to be able to print their values as a plain HTML list in a div with ID combined. Right now, I have the following code:
function doCombine() {
document.getElementById('combined').innerHTML =
document.getElementsByClassName('output').value + ",";
}
Yet, when I run the function, i get the error message undefined,. When i add a [0] before .value, it works, but only the value of the first textbox is showing up. I read somewhere that [i] will show all the values, but that doesn't seem to work.
What am I doing wrong?

getElementsByClassName
Returns a set of elements which have all the given class names. When called on the document object, the complete document is searched, including the root node. You may also call getElementsByClassName on any element; it will return only elements which are descendants of the specified root element with the given class names.
So you should be doing
var elements = document.getElementsByClassName('output');
var combined = document.getElementById('combined');
for(var i=0; i < elements.length; i++) {
combined.innerHTML += elements[i].value + ",";
}

getElementsByClassName returns an array-like object, not a single element (notice the plural in the name of the function). You need to iterate over it, or use an array index if you just want to operate on the first element it returns:
document.getElementsByClassName('output')[0].value + ","

getElementsByClassName returns a set of elements. You need to iterate over it:
var elems = document.getElementsByClassName("output");
for(var i=0; i<elems.length; i++) {
combined.innerHTML += elems[i].value + ",";
}
That's why adding [0] works, because you are accessing the first object in this set.

This function will return ALL the elements with that name, because"name" attribute is not unique, so it returns an list (nodeList, to be exact).
To print out ALL the values, you need to add a loop. Something like
var finalvar = "";
var arr = document.getElementsByClassName('output');
for (i=0;i<arr.length;i++) {
finalval = finalval + arr[i].value;
}
document.getElementById('combined').innerHTML = finalval

getElementsByClassName will return a set of elements. Refer: https://developer.mozilla.org/en-US/docs/Web/API/document.getElementsByClassName#Summary. Some browsers return HTMLCollection and some browsers return NodeList. https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection#Browser_compatibility But they both have length property and item method in common. So you can iterate like this.
function doCombine()
{
var listOfOutputElements = document.getElementsByClassName('output');
var combinedItem = document.getElementById('combined');
for (var i = 0; i < listOfOutputElements.length; i += 1) {
combinedItem.innerHTML += listOfOutputElements.item(i).innerHTML;
}
}

Try this :
<script type="text/javascript">
function doCombine()
{
var combined = document.getElementById('combined');
var nodeList = document.getElementsByClassName('output');
var nodeListLength = nodeList.length;
for (i=0;i<nodeListLength;i++) {
combined.innerHTML += nodeList[i] + ',';
}
</script>

getElementsByClassName returns an NodeList. So you won't be able to call the value method on it. Try the following:
function doCombine() {
var combined = document.getElementById('combined');
var outputs = document.getElementsByClassName('output');
for(var i=0; i<outputs.length; i++){
combined.innerHTML += outputs[i].value + ',';
}
}
http://jsfiddle.net/FM3qH/

Related

When parsing XML with recursive function, how do I return a string or array from that function?

I've got a working recursive function which goes through an XML doc looking for a matching node name, and then logging matching values...I'm trying to modify it to return a string or an array, and can't figure it out.
This is in Google Apps script. I've tried passing in a blank string into the function, and then returning it at the end, but it doesn't work. Here is the working Logger function:
function logChildren(elements, dataRequired){
for (var i = 0; i < elements.length; i++) {
if (elements[i].getName() == dataRequired){
Logger.log(elements[i].getText());
}
if(elements[i].getContentSize() > 1){
var children = elements[i].getChildren();
logChildren(children, dataRequired);
}
}
};
I tried passing in an empty string, and then returning it like this but it doesn't work:
function logChildren(elements, dataRequired, str){
for (var i = 0; i < elements.length; i++) {
if (elements[i].getName() == dataRequired){
str = str + ", " + elements[i].getText();
}
if(elements[i].getContentSize() > 1){
var children = elements[i].getChildren();
logChildren(children, dataRequired, str);
}
}
return str
};
How do I get a string or array OUT of this function, rather than just console logging it?
Instead of returning str try without it, because str will have all the values. If you return str it might collapse the current iteration. Please let us know whether this worked
Providing your elements is already parsed and valid, this should work.
function logChildren(elements, dataRequired){
values = [];
req = elements.getElementsByTagName(dataRequired);
for (var i = 0; i < req.length; i++) {
values.push(req[i].childNodes[0].nodeValue);
}
return values
};
elements = "<house>" +
"<name>hello</name>" +
"<town>world</town>" +
"<name>cat</name>" +
"<folder>" +
"<name>kitty</name>" +
"</folder>" +
"</house>";
p = new DOMParser();
elements = p.parseFromString(elements, "text/xml");
newValues = logChildren(elements, "name")
console.log(newValues);
I've included my own little xml just to test, and it returns an array.
As you can see, getElementsByTagName even returns values in sub folders.
You should use a global variable or another function, so that the output variable str is outside the scope of the recursed function.
var str = "";//holds all data of recursion
function logChildren(elements, dataRequired){
..
str += ", " + elements[i].getText();
..
}

Javascript For loop appending child only appends first element, then throws error

I'm looping through a js object with a nested for loop, stated below, it appends the first element correctly, but then throws the following error:
Can't set the property className of an undefined reference or empty reference. (not sure if exact error, translating from Dutch...)
function allVideos() {
var sql = "SELECT videos.VideoName, videos.VideoPath FROM videos";
var resultSet = db.query(sql, {json:true}); //returns: [{"VideoName":"timelapse aethon2","VideoPath":"videos\\Roermond Papier\\160424 Time laps Aethon2.avi"},{"VideoName":"timelapse aethon3","VideoPath":"videos\\Roermond Papier\\160424 Time laps Aethon2.avi"}]
var parsed = JSON.parse(resultSet);
var parsedlength = arrLenght(parsed);
//alert(resultSet);
for(var i = 0; i < parsedlength; i++) {
var obj = parsed[i];
//alert(i);
var videoElement = document.getElementById("allVideos");
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
videoElement.appendChild(document.createElement('div'));
videoElement.children[i].id='allVid' + i;
videoElement.children[i].className='col-md-4 col-xs-12';
//alert(typeof key)
var card = document.getElementById('allVid' + i);
alert(i);
card.appendChild(document.createElement('div'));
card.children[i].className='card card-block';
card.children[i].innerHTML = "<h3 class='card-title'>" + obj['VideoName'] + "</h3><button class='btn btn-primary'>Selecteren</button>"
}
}
}
}
[EDIT] added screenshot of how it looks
Your code has some significant logic issues. You're using nested loops, but appending to an element assuming that the outer loop counter will let you index into that element's children to get the element you just appended. Later, you try to get that same element again using getElementById. Then, you append a new element to your newly-created element, but try to access that new element using children[i] on the one you just created — at that point, the card element will only have a single child, so as of the second outer loop, it will fail.
createElement returns the element to you, so there's no reason at all to try to access it via children[i] (either time) or getElementById.
See comments:
function allVideos() {
var sql = "SELECT videos.VideoName, videos.VideoPath FROM videos";
var resultSet = db.query(sql, {json:true});
var parsed = JSON.parse(resultSet);
var parsedlength = arrLenght(parsed);
for(var i = 0; i < parsedlength; i++) {
var obj = parsed[i];
//alert(i);
var videoElement = document.getElementById("allVideos");
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
// Create the card, give it its id and class
var card = document.createElement('div');
card.id='allVid' + i;
card.className='col-md-4 col-xs-12';
// Create the div to put in the card, give it its class and content
var div = document.createElement('div');
card.appendChild(div);
div.className='card card-block';
div.innerHTML = "<h3 class='card-title'>" + obj['VideoName'] + "</h3><button class='btn btn-primary'>Selecteren</button>"
// Append the card
videoElement.appendChild(card);
}
}
}
}
Side note: arrLenght looks like a typo (it should be th, not ht), but moreover, there's no reason to use a function to get the length of an array; it's available via the array's length property: parsedLength = parsed.length.
Side note 2: You may find these ways of looping through arrays useful.
Your problem is the if within the nested for:
if(obj.hasOwnProperty(key)) { ...
The variable i is increased even if the property is not "owned" (when the if condition returns false), so next time that the condition is true, i is out of bounds.

Using innerHTML with querySelectorAll

I want to use something similar to the following to clear errors in a form upon a resubmission attempt:
document.querySelectorAll("#form-error-name, #form-error-email, #form-error-tel, #form-error-dob, #form-error-password, #form-error-goal").innerHTML= "";
...But the contents of the divs isn't cleared. What am I doing wrong?
The question wants .innerHTML, but that's not valid on input; you actually want .value. The appropriate modern answer would be
[... document.querySelectorAll('input')]
.map(i => i.value = '');
You'll need to loop through the results
var errors = document.querySelectorAll(
"#form-error-name,
#form-error-email,
#form-error-tel,
#form-error-dob,
#form-error-password,
#form-error-goal");
[].forEach.call(errors, function(error) {
error.innerHTML = '';
});
querySelectorAll doesn't return an array, but a node list, which doesn't have a forEach method on its prototype.
The loop above is using the forEach method on the array object's prototype on the nodeList object.
Try this:
var x = document.querySelectorAll("#form-error-name, #form-error-email, #form-error-tel");
var i;
for (i = 0; i < x.length; i++) {
x[i].innerHTML = "";
}
See, if that helps.
Another way to do this is to call forEach directly on the results of querySelectorAll, which returns NodeList resutls:
query_selector = "#form-error-name, #form-error-email, #form-error-tel, #form-error-dob, #form-error-password, #form-error-goal"
document.querySelectorAll(query_selector).forEach(node => node.innerHTML = "")

Why is element.tagName undefined?

I was having a little play around this morning and I thought it would be fun to do something silly and try and write all the tagnames of a page on that page using something like this
var elements = document.getElementsByTagName("*");
for(var element in elements) {
document.write(element.tagName + "<br />");
}
However all that gets written is undefined. Why is this? Oh and I'm using Firefox.
for...in is not guaranteed to work for a NodeList (which is what document.getElementsByTagName() returns), and even when it does, what you'll be getting as the value of element will be not be a reference to the element but rather a property name, such as the numeric index of the element in the NodeList. Use a normal for loop instead.
var elements = document.getElementsByTagName("*");
for (var i = 0; i < elements.length; ++i) {
document.write(elements[i].tagName + "<br />");
}
Also, document.write() won't work as you might expect after the document has loaded, which is when you'd typically use document.getElementsByTagName(). A better mechanism would be to populate an element in the page:
<div id="debug"></div>
Script:
var elements = document.getElementsByTagName("*");
var debugDiv = document.getElementById("debug");
var tagNames = [];
for (var i = 0; i < elements.length; ++i) {
tagNames.push(elements[i].tagName);
}
debugDiv.innerHTML = tagNames.join("<br>");
Because you're using a for...in loop, element is actually the item's key and not the value. elements[element] would give you the correct result, but you should use a proper for loop instead:
for (var i = 0; i < elements.length; i++) {
document.write(elements[i].tagName + "<br />");
}
This is because for...in may iterate over other enumerable properties that are not elements of the collection. These properties can differ between browsers.
You could try:
var elements = document.getElementsByTagName("*");
for (int i = 0; i < elements.length; i++)
{
document.write(elements.item(i).tagName + "<br />");
}
I suppose that is impossible, and is possible with jQuery/Sizzle HTML/CSS Selector:
$('*');
With that function/method you can only select "all available HTML tags" in your current document.
For example:
document.getElementByTagName('html');
document.getElementByTagName('head');
document.getElementByTagName('body');
document.getElementByTagName('h3');
document.getElementByTagName('p');
document.getElementByTagName('pre');
document.getElementByTagName('code');
document.getElementByTagName('metaforce'); // being a custom HTML(5) tag

I want to loop through an array and modify attributes

Here is my code
var input_buttons = ["#one","#two","#three"];
var substr = input_buttons.split(',');
for(var i=0; i< substr.length; i++)
{
substr.attr('value', '');
}
Why doesn't this work?
Your first problem is calling split(',') on an array. However, if you just want to set the values of all those to a blank string you can do:
$('#one,#two,#three').val('');
If you want to set different values you'd need to loop through:
$('#one,#two,#three').each(function() {
// this == the HTML node (not a jQuery element)
this.value = someValue; // someValue would set outside
};
You already have an array, there is nothing to split, this only works on strings. You'd also have to pass the ID to jQuery before you can cal attr. In this case val is even better.
var input_buttons = ["#one","#two","#three"];
for(var i=input_buttons.length; i--;) {
$(input_buttons[i]).val('');
}
But shorter would be using the multiple selector:
$('#one, #two, #three').val('');
or if you already have the array, create a string by joining the IDs:
$(input_buttons.join(',')).val('');
I'm wondering why you are calling:
var substr = input_buttons.split(',');
By the nature of your input_buttons, you already have an array. All you should have to do is:
var input_buttons = ["#one","#two","#three"];
for(var i=0; i< substr.length; i++)
{
$(input_buttons[i]).attr('value', '');
}
var input_buttons = ["#one","#two","#three"];
$.each(input_buttons, function(idx, value) {
$(value).val('');
});
Or even better and shorter:
$('#one, #two, #three').val('');
You could also give those elements a common class name and then use this:
$('.className').val('');
your array contains just the id but not the actual object
try this
var input_buttons = ["#one","#two","#three"];
for(var i=0; i< input_buttons.length; i++)
{
$(input_buttons[i]).removeAttr('value');
}
input_buttons is already an array - don't split it.
To use .attr you need it to be a jquery object, so call $(input_buttons[i]).attr
Try the following to remove an attribute:
var input_buttons = ["#one","#two","#three"];
for(var i=0; i< input_buttons.length; i++)
{
$(input_buttons[i]).removeAttr('value');
}
The reason your code does not work is in the overloading of jQuery functions. .attr('value', '') evaluates to .attr('value'), which returns the value of value as opposed to setting it. The reason is that '' evaluates to false.

Categories

Resources