I am new to javascript. When i headed with the javascript events i have seen about element.classList.So i have tried out some code to understand how it works ..
The code i have done
var n = document.getElementsByClassName('name');
var c = n.classList.add('name');
console.log(c);
it gives me error like Uncaught TypeError: Cannot read property 'add' of undefined.
Can anyone point me where am going wrong ??
getElementsByClassName returns a NodeList (which is like an Array). Note that "Elements" is plural.
classList is a property of an Element (singular)
You need to loop over the Elements in the NodeList and access the classList property of each one in turn.
for (var i = 0; i < n.length; i++) {
console.log( n[i].classList.add('name') );
}
Related
I am writing a bit of JS code to switch classes for certain DOM elements.
Everything is working as it should, but I am also getting this error and this
prevents the code that follows to be executed.
I added a check to make sure that the array of elements is not empty as I thought
this was the problem. But still the error occurs. Debugging always showed a value for old_name when replace is called on it. Am I missing something JS-esque?
This is the part of the code that causes the error, specifically line 31:
if (w > MOBILE_THRESHOLD) {
responsive_elements = document.getElementsByClassName("desktop");
if (responsive_elements.length > 0) {
for (i in responsive_elements) {
var old_name = responsive_elements[i].className;
var new_name = old_name.replace("desktop", "mobile");
responsive_elements[i].className = new_name;
}
}
When necessary I will be happy to provide more code, or whatever information is needed. Thanks!
Never use for...in loops to iterate array-like objects!!!!!
In this case, the HTMLCollection has enumerable properties like item or namedItem. Accessing these won't return an HTML element, so className will be undefined.
To iterate properly, you can use one of these
for(var i=0; i<responsive_elements.length; ++i) {
// ...
}
[].forEach.call(responsive_elements, function(element, index, collection) {
// ...
});
I am using getElementById when I need to clone the div element.
Code:
printHTML( document.getElementById("div_name").cloneNode(true));
Now I need to use getElementsByClassName
CloneNode is not working when using getElementsByClassName.
How can I put class name in here?
Thank's
EDIT:
When I try to use this:
printHTML( $('.dataTables_scroll').clone(true) );
You can see my function:
function printHTML(clonedDive){
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.contentWindow.onunload = function(){
$(".DTTT_container").show("fast");
$("#header_outer").show("fast");
$(".ColVis.TableTools").show("fast");
$("#footer").show("fast");
};
iframe.contentWindow.document.body.appendChild(clonedDive);
iframe.contentWindow.print();
document.body.removeChild(iframe);
}
I am getting an error in this line:
iframe.contentWindow.document.body.appendChild(clonedDive);
This is an error description:
Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
getElementsByClassName gets a nodelist, or an array-like object containing elements if you will, as there can be more than one element with the same class.
getElementsByClassName does this even if only one element matches the class.
You can generally recognize methods like that be the s in getElements, which means it gets multiple elements, i.e. a nodeList.
getElementById only gets one element as ID's are unique.
To get the first element in the nodelist, use bracket notation, like so:
document.getElementsByClassName("div_name")[0].cloneNode(true);
or one could use querySelector, which gets the first matching element only
document.querySelector(".div_name").cloneNode(true);
The jQuery solution would be:
$('.div_name').clone(true);
and to iterate over elements with a certain classname you'd use a loop
var elems = document.getElementsByClassName("div_name");
for ( var i=0; i<elems.length; i++ ) {
printHTML( elems[i].cloneNode(true) );
}
Due to getElementsByClassName returns an object's array, so you have to use for loop to iterates among them, as follows:
for (i = 0; i < document.getElementsByClassName("div_name").length; i++){
printHTML( document.getElementsByClassName("div_name")[i].cloneNode(true));
}
otherwise, if you know the index of the element you have let we say 1
printHTML( document.getElementsByClassName("div_name")[1].cloneNode(true));
This does not work? :
printHTML( document.getElementsByClassName("class_name")[0].cloneNode(true));
You can loop through the elements and clone one by one...
var e = document.getElementsByClassName('div');
for (var i = 0; i < e.length; i += 1) {
// Clone e[i] here
console.log(e[i].cloneNode(true));
}
If I use get with defineProperty
Object.defineProperty(Object.prototype,'parent',{
get:function(){return this.parentNode}
});
and I can call it like: document.body.parent, then it works.
When I use value with defineProperty
Object.defineProperty(Object.prototype,'parent',{
value:function(x){
var temp=this.parentNode;
for(var i=1;i<x;i++){temp=temp.parentNode};
return temp
}
});
I can call it like: document.getElementsByName("newtag").parent(2), means to find the parent node of newtag's parent node.
But when I put them together it says Uncaught TypeError: Invalid property. A property cannot both have accessors and be writable or have a value.
How can I do it so that I can call it both ways, .parent & .parent(n)?
No jQuery
MDN describes the reason for the error
Property descriptors present in objects come in two main flavors: data
descriptors and accessor descriptors. A data descriptor is a property
that has a value, which may or may not be writable. An accessor
descriptor is a property described by a getter-setter pair of
functions. A descriptor must be one of these two flavors; it cannot be
both.
There is reason for this: the way you want would be ambiguous: if parent() was a function, then parent would return that function and the getter?
Also do not change object that you don't own. Such code would not be maintainable: if someone would define its own Object.prototype.parent() in his library, you could not use it. Prior to any use of any code, you would need to track down what was changed. This would be the same effort as writing everything from scratch.
Object.prototype is particularly bad idea to change: by the prototype you add the parent() function to every array, JSON object, webAPI objects... they don't have parentNode, so that function is completely useless to them, it is just a performance burden.
The previous two paragraphs are the reason why we have Object.defineProperty and not Object.prototype.defineProperty. Note that if it would be so, you could code myAPI.defineproperty(...) in the code below, which is shorter, BUT... the performance and the design... schrecklich.
You could code something like this
var myAPI = {
node: document.body,
level: 1
};
Object.defineProperty(MyAPI,'parent',{
get:function(){
var temp=MyAPI.node.parentNode;
// some sanity check should be performed here
for(var i=1;i<MyAPI.level;i++){temp=temp.parentNode};
return temp;
}
});
myAPI.node = document.getElementById("myNode");
myAPI.level = 2;
var grandFather = myAPI.parent; // actually returns the grandparent
but I doubt it would be useful.
No, you can't unfortunately. Instead you can create a bit different function that returns the first parentNode by default(if no parameter specified), otherwise it counts n parents.
Object.prototype.parent = function (n) {
if (!this.hasOwnProperty('parentNode')) return null;
if (!n || n === 1) return this.parentNode;
var temp = this.parentNode;
for (var i = 1; i < n; i++)
temp = temp.parentNode;
return temp;
};
Some notes: first of all, you should check if the Object has a property parentNode, otherwise the script will raise an exception. I used hasOwnProperty, but you can remove that line if you extend just HTMLElement.
Also, if n is 0 or 1, or it is not defined it will return the element's parentNode. Use it as element.parent() or element.parent(2).
I have a function that is called when an element is clicked.
function showHide(elem){
var clickedText = elem.getElementsByTagName("p");
clickedText.style.color = "green";
}
When I run this code I get an Uncaught TypeError: Cannot set property 'color' of undefined. However, if I do console.log(clickedText) it logs the appropriate tag. Can't for the life of me figure out why this isn't working, have I missed something totally obvious?
The getElementsByTagName method returns a collection of elements (in a NodeList, which is like an array).
You will need to specify an index. For example:
clickedText[0].style.color = "green";
Currently, you're trying to access the style property of the NodeList itself, rather than an element contained within it.
getElementsByTagName returns a list of elements, so you need to loop through the results:
var clickedTexts = elem.getElementsByTagName("p");
for (var i = 0; i < clickedTexts.length; i++) {
clickedTexts[i].style.color = "green";
}
Hopefully I can ask this in an understandable way...
Overall, I am trying to determine what type of object I am currently dealing with.
I'm creating a collection (HTML is example, not literal) and I need to filter my collection to certain elements eg:
<div id="tabContentWrap">
<div id="tab">
Link Element<img src="img.jpg" alt="img" />
<select id="my_select"><option value="1">1</option></select>
</div>
</div>
function getFilteredElements() {
var tabContent = getElementsByClass("tabContentWrap", document.getElementById(tabWrapId), "div");
for (var j = 0; j < tabContent.length; j++){
tabContentLinks = tabContent[j].getElementsByTagName('*');
for (var k = 0; k < tabContentLinks.length; k++){
// Here i attempt to filter the collection
if (tabContentLinks[k] == '[object HTMLSelectElement]') {
alert("found select list");
}
}
}
}
Which works fine in Mozilla but not in Internet Explorer 8, tabContentLinks[k] returns [object] instead of [object 'ObjectType']
So I investigated and discovered that you can use Object.prototype.toString.call(object) to get the object type, which again works fine in Mozilla but returns [object Object] in IE8...
I call
get_type(tabContentsLink[k]);
which runs the following function:
function get_type(thing){
if (thing === null) return "[object Null]";
// special case
return Object.prototype.toString.call(thing);
}
But this just returns [object Object]
Does Object.prototype.toString.call() ever return the type of object in IE or am I very far off and barking up a lamppost instead of a tree?
Well, first of all I want to tell you that Object.prototype.toString returns the value of the object's internal [[Class]] property, it isn't really a Type.
The value of this internal property represents the specification defined classification of an object (more info here).
Javascript has only 6 language types: Object, String, Number, Boolean, Null and Undefined, that's it.
The value of the [[Class]] internal property for host objects -as DOM elements- can be anything, it is completely implementation-dependent, on built-in objects is safe to use it -except with some exceptions in IE as #Alex pointed out in the article linked in his answer-.
You are working with DOM elements, and you want to figure out what kind of node it is, my suggestion to this, is to simply use the nodeName property (please avoid using tagName).
The nodeName property contains the name of the node you are dealing it, in upper case, therefore you could use it as this:
function getFilteredElements() {
var tabContent = getElementsByClass("tabContentWrap",
document.getElementById(tabWrapId), "div");
for (var j = 0; j < tabContent.length; j++){
tabContentLinks = tabContent[j].getElementsByTagName('*');
for (var k = 0; k < tabContentLinks.length; k++){
// Here i attempt to filter the collection
if (tabContentLinks[k].nodeName == 'SELECT') { // <- SELECT elements
alert("found select list");
}
}
}
}
Rather than recreate the entire discussion and possible solutions, I'll just point you to a blog post that discusses this exact issue.
I believe IE will return simple data types, but everything else is just an object. It's a browser limitation. I don't know if IE9 is improving the situation, but that probably wouldn't help you anyway.
I don't have IE handy, but the proper way in Firefox is to use object.constructor.name (object.constructor being the object's constructor, .name being the function name).
function Xyz() {}
new Xyz().constructor.name === 'Xyz'
But then, also,
var Abc = function() {}
new Abc().constructor.name === 'Abc'