variable turns to undefined inside a for loop - javascript

I'm am battling with a javascript function I'm working on.
Inside a for loop I'm iterating all elements with class "visible", inside that loop
I'm preforming two actions.
elements[i].removeAttribute("class");
elements[i].setAttribute("class", "hidden");
For some reason only 1 is valid. 2 produces an error saying:
Uncaught TypeError: Cannot call method 'setAttribute' of undefined
Even when I log elements[i] using console.log; after the first console.log call
the element exists, but on the second console.log elements[i] is 'undefined'
What the hell am I missing here, this is driving me crazy, if my laptop wasn't so expensive
it would have been broken by now. Help :(
Here's the function:
function hide_visable_elements()
{
// remove body EventListener
var body = document.getElementsByTagName("body");
body[0].removeEventListener("click", hide_visable_elements, true);
var elements = document.getElementsByClassName("visible");
for (var i = 0; i < elements.length; i++)
{
console.log(elements[i]); // Works like a swiss clock
elements[i].removeAttribute("class");
console.log(elements[i]); // why elements[i] is 'undefined' now ???
elements[i].setAttribute("class", "hidden"); // << turns to useless code
}
}

This is because getElementsByClassName returns a NodeList, which is live. That is, it updates itself when the elements it refers to change.
When you remove the class attribute from an element in the NodeList, it gets removed from that list (since it no longer has the visible class name).
You don't actually need to remove the attribute. Just setting it will do the job just as well. But since the NodeList is changing as you manipulate the elements it contains, you need to count backwards through it (as each time you change one element of it, it is removed so the length decreases by one):
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].setAttribute("class", "hidden");
}

getElementsByClassName is a live NodeList so changing className of the items immediately affects whole list. I would recommend use querySelectorAll insead.
Plus instead of var body = document.getElementsByTagName("body"); use document.body.

I think that the problem is elements[i].removeAttribute("class"); since you selected the element using a class getElementsByClassName("visible"); . I think so when you remove class attribute completely from the element things are going wrong.
Try some tweak with the code. You are not suppose to remove attribute class if you are planning to use the same element which is selected using class attribute.

Related

looping over array of elements just affects the last element

In one of my projects I just discovered, that sometimes iterating over an array of html elements (and change all of them) just affects the last element. When I log the element's attributes I can see that the loop definitily adresses every element but nevertheless visibly just the last element is getting changed.
Can anyone explain me why?
I already figured out, that a solution is to use createElement() and appendChild() instead of insertHTML. I just want to understand why javascript behaves like this.
Here is my example code:
/* creating 5 elements and storing them into an array */
var elementArray = [];
for(var n = 0;n<5;n++)
{
document.body.innerHTML += "<div id='elmt_"+n+"'>"+n+"</div>\n";
elementArray[n] = document.getElementById("elmt_"+n);
}
/* loop over these 5 elements */
for(var n = 0;n<5;n++)
{
console.log(elementArray[n].id); // logs: elmt_0 elmt_1 elmt_2 elmt_3 elmt_4
elementArray[n].innerHTML = "test"; // changes just the last element (elmt_4) to "test"
}
I created an example here: http://jsfiddle.net/qwe44m1o/1/
1 - Using console.log(elementArray[n]); in your second loop shows that innerHTML in this loop is modifying html inside your array, not in your document. That means that you are storing the div element in your array, not a shortcut to document.getElementById("elmt_"+n)
See the JSFiddle
2 - If you want to store a shortcut in order to target an element by ID, you have to add quotes for elementArray[n] = "document.getElementById('elmt_"+n+"')";, and use it with eval like this : eval(elementArray[n]).innerHTML = n+"-test";
See the JSFiddle for this try

Finding the index of the element with class in native Javascript

Is there a way to get the index of class name (I.e. the third element with the class "className" would be 3 without using jQ?
I don't know jQ, and I don't have time to learn it right now, and I don't want to include code into my code that I don't understand at least some.
Thanks.
BTW, I've used jQ instead of spelling it out so those results can be filtered out in Google should somebody have the same question. If I spelled it out, and somebody used the NOT operator in Google, this one would also disappear.
You could do something like:
// the element you're looking for
var target = document.getElementById("an-element");
// the collection you're looking in
var nodes = document.querySelectorAll(".yourclass");
var index = [].indexOf.call(nodes, target);
See: Array's indexOf.
If you have already a proper array as nodes instead of a NodeList, you can just do nodes.indexOf(target).
you can use document.getElementsByClassName
var el = document.getElementsByClassName('className');
for (var i = 0; i < el.length; i++) {
// your index is inside here
}
el[i] is the element in the current iteration, i is the index
(I.e. the third element with the class "className" would be 3)
if (i == 3)
return el[i]
JsFiddle: here.
Just use getElementsByClassName, it returns a list of elements with the specified classes.
elements = document.getElementsByClassName("test")
element = elements[2] //get the 3rd element
Hope this helps!
these work as of es6:
Array.from(document.querySelectorAll('.elements')).indexOf(anElement)
or
[...document.querySelectorAll('.elements')].indexOf(anElement)

Javascript array not working as expected

I'm pretty new to js/jquery. For each checkbox with the ID of check$ (where $ is a sequential number), I want to toggle the class "agree" of the surrounding span that uses the same check$ (but as a class). I don't want to have to hard-code the list of matching checkboxes, as this may vary.
Here's my code. This function works as expected:
agree = function (checkbox, span) {
$(checkbox).change(function(){
$(span).toggleClass('agree');
});
};
This is what I'm trying to pass to the above function, which does not work:
$(function() {
var elemid = 'check',
checks = Array($('[id^='+elemid+']').length);
console.log(checks);
for (i=0; i < checks; i++) {
agree('#'+elemid+checks[i], "."+elemid+checks[i]);
}
});
console.log(checks) returns [undefined × 4]. The number of elements is correct, but I don't know why it's undefined, or whether that is even significant.
The following code works as expected, but as I say, I'd rather not have to specify every matched element:
$(function() {
var checks = ["check1", "check2", "check3", "check4"];
for (i=0; i < checks.length; i++) {
agree('#'+checks[i], "."+checks[i]);
}
});
Thanks.
Edit: Thanks to Jack, I was overlooking the most simple method. I added the same class to all checkboxes and spans, and solved the problem with this:
$('input.check').change(function(){
$(this).closest('span.check').toggleClass('agree');
});
I might be totally missing something, but I'm pretty sure you are just trying to attach a change handler to each checkbox. In this case you can give them all the same class. I'm also guessing at your html structure for the span.
For reference:
http://api.jquery.com/closest/
http://docs.jquery.com/Tutorials:How_jQuery_Works
$('.yourcheckboxclass').change(function(){ //grab all elements with this class and attach this change handler
$(this).closest('span').toggleClass('agree');
});
The reason that the array is full of undefined values, is that you are just getting the number of items in the jQuery object, and create an array with that size. The jQuery object is discarded.
Put the jQuery object in the variable instead:
var elemid = 'check', checks = $('[id^='+elemid+']');
checks.each(function(){
agree(this, "."+elemid+checks[i]);
});

how to access properties of document.getElementsByClassName

I am trying to check whether the a css class is used inside the DOM or not. So, I have
var x = document.getElementsByClassName('classname');
When I print x out, I get a [object NodeList] for classes that exist on the page and classes that dont. Is there a property of x that I can access ? Like the tag name or something. Would be great if somebody can tell me the different properties of x and the ways I can access them.
Thank you :)
Notice that it's plural:
var x = document.getElementsByClassName('classname');
^
You need to iterate over x to get the individual elements:
for (var i = 0; i < x.length; i++) {
var element = x[i];
console.log(element);
}
Make sure to add fallback support for Internet Exploder: http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
If you just want to check for the presence of a class in the document, you can also use querySelector.
var x = document.querySelector('.classname');
It returns null if no elements have that class, otherwise the first element with that class name. If you want all elements using classname:
var x = document.querySelectorAll('.classname');
Now x is null if the class is not used in the document, otherwise a Nodelist, containing all elements with class classname, which can be iterated the way Blender showed. In that iteration you can, for example, retrieve the elements tagName or its id. Like:
for (var i=0;i<x.lenght;(i=i+1)){
console.log(x[i].id + ': ' + x[i].tagName);
}
document.querySelector is available in all modern browsers, and for IE version 8 and up.
I almost always use document.querySelector (which: "Returns the first element within the document that matches the specified group of selectors"), which returns an element object and it's.
I don't know why but in my Chrome's console I write:
var img = document.getElementsByClassName('image__pic');
img[0]...
img[0], despite its happy existance, it doesn't generate any further attributes/methods to use in the completion window. Like there were none (even though I could use img[0].src for instance)
On the other hand:
var imgq = document.querySelector('.image__pic')
Gives me very useful autocompletion on the Console:
As far as its browser support it is phenomenal:
It is also less tricky to use, because getElementsByClassName returns an HTMLCollection, which is a little beast of its own.
Another plus for querySelector is its versatility: any kind of CSS selector goes!
On the negative side, querySelector is a bit slower, but I think it's worth it.

Remove element created by JavaScript on load

How can I remove elements which are created by JavaScript, I tried using CSS by setting display:none; but that doesn't seem to solve my problem, also I can't remove them since I don't have them in HTML, any other ways? Thank you
UPDATE:
Can't use any JavaScript such as jQuery, MooTools, ExtJS etc, and actual elements I want to remove are divs, with a specified class so I can't use getElementById.
I found this script on Google, but it doesn't seem to work but this is what I need:
HERE
This is fairly simple to do this using jQuery.
$("#myId").remove();
will remove
<div id="myId"></div>
Edit: You can also do it with "old school" javascript.
The function you're looking for is removeChild()
Example:
function removeElement(divNum) {
var d = document.getElementById('myDiv');
var olddiv = document.getElementById(divNum);
d.removeChild(olddiv);
}
You will want something like this to take advantage of browser support where you can:
if(document.getElementsByClassName){
// Safari 3+ and Firefox 3+
var itms = document.getElementsByClassName('your_class');
for(var i = 0; i<itms.length; i++){
var it = itms[i];
if(it.tagName == "DIV"){
it.parentNode.removeChild(it);
i = i - 1; //It actually gets removed from the array, so we need to drop our count
}
}
} else {
// All other browsers
var itms = document.getElementsByTagName('div');
for(var i = 0; i<itms.length; i++){
var it = itms[i];
// Test that className contains your class
if(/your_class/.test(it.className)) it.parentNode.removeChild(it);
}
}
JavaScript handles all memory mangement for you using garbage collection so once all references to an object cease to exist that object will be handled by the browsers specific implementation.
If you have the dom element itself:
if(node && node.parentNode){
// return a ref to the removed child
node.parentNode.removeChild(node);
}
Since you say you can't use Javascript, you're pretty stuck. Have you tried this CSS:
.classname {
display: none !important;
}
It's possible that the created elements have inline styles set on them, in which case normal CSS is overridden. I believe the !important will override inline styles.
Of course, the best solution is not to add the element in the first place... but I'm guessing you're in one of those (unfathomably common) scenarios where you can't change or get rid of the JS?
Not all browsers have a
document.getElementsByClassName
method, but for your purpose you can
fake it well enough- This method does
not work like the native
HTMLElement.getElementsByClassName- it
returns an array, not a live nodelist.
You can specify a parent element and a
tag name to speed it up.
function getElementsByClass(css, pa, tag){
pa= pa || document.body;
tag= tag || '*';
css= RegExp('\\b'+css+'\\b');
var A= [], elements, L, i= 0, tem;
elements= pa.getElementsByTagName(tag);
L= elements.length;
while(i<L){
tem= elements[i++];
if(css.test(tem.className)) A[A.length]= tem;
}
return A;
}
// test
var A= getElementsByClass('removeClass'), who;
while(A.length){
who= A.pop(); // remove from the end first, in case nested items also have the class
if(who.parentNode) who.parentNode.removeChild(who);
who= null;
}
If you have assigned event handlers to
the elements being removed, you should
remove the handlers before the
elements.
You will probably have to be more specific.
The general answer is 'with JavaScript'. As long as you have a way of navigating to the element through the DOM, you can then remove it from the DOM.
It's much easier if you can use a library like jQuery or prototype, but anything you can do with these you can do with JavaScript.
marcgg has assumed that you know the ID of the element: if you don't but can trace it in the DOM structure, you can do something like this (in prototype - don't know jQuery)
var css_selector = 'table#fred tr td span.mydata';
$$(css).invoke('remove');
If you can't use a JS library, you'll have to do the navigation through the DOM yourself, using Element.getElementsByTagName() a lot.
Now you've specified your question a bit: use Element.getElementsByTagName, and loop through them looking at their className property.
Use:
document.removeChild('id_of_element');

Categories

Resources