My goal: Test if the attribute of an object is/returns true. However, in some cases, the object is undefined.
This works no problem. The script continues normally.
if(somethingUndefined){ }
However, if I try to access an attribute of an undefined object, this generates an error and stops the script.
if(somethingUndefined.anAttribute){ }
Right now, this is what I'm using to solve the problem:
if(somethingUndefined && somethingUndefined.anAttribute){ }
Is there another way to do that? Maybe a global settings that will return false if the program tries to access an attribute of an undefined object?
If you have many if statement like if(somethingUndefined && somethingUndefined.anAttribute){ }, then you could assign an empty object to it when it is undefined.
var somethingUndefined = somethingUndefined || {};
if (somethingUndefined.anAttribute) {
}
You can take advantage of JavaScript's ability to assign variables within if conditions and follow this pattern for faster checks once you get past the first nested object.
JsPerf
var x;
if(
(x = somethingUndefined) && // somethingUndefined exists?
(x = x.anAttribute) && // x and anAttribute exists?
(x = x.subAttrubute) // x and subAttrubute exists?
){
}
vs the traditional
if(
somethingUndefined && // somethingUndefined exists?
somethingUndefined.anAttribute && // somethingUndefined and anAttribute exists?
somethingUndefined.anAttribute.subAttribute // somethingUndefined and anAttribute and subAttribute exists?
){
}
The way you have it in your question is generally the way it's done in javascript. If you find yourself using this a lot, you could abstract it out into a function to make things a tiny bit cleaner for yourself, as such:
if (attrDefined(obj, 'property')) {
console.log('it is defined, whoo!');
}
function attrDefined(o, p){ return !!(o && o[p]) }
Related
How can I check if one DOM element is a child of another DOM element? Are there any built in methods for this? For example, something like:
if (element1.hasDescendant(element2))
or
if (element2.hasParent(element1))
If not then any ideas how to do this? It also needs to be cross browser. I should also mention that the child could be nested many levels below the parent.
You should use Node.contains, since it's now standard and available in all browsers.
https://developer.mozilla.org/en-US/docs/Web/API/Node.contains
Update: There's now a native way to achieve this. Node.contains(). Mentioned in comment and below answers as well.
Old answer:
Using the parentNode property should work. It's also pretty safe from a cross-browser standpoint. If the relationship is known to be one level deep, you could check it simply:
if (element2.parentNode == element1) { ... }
If the the child can be nested arbitrarily deep inside the parent, you could use a function similar to the following to test for the relationship:
function isDescendant(parent, child) {
var node = child.parentNode;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
I just had to share 'mine'.
Although conceptually the same as Asaph's answer (benefiting from the same cross-browser compatibility, even IE6), it is a lot smaller and comes in handy when size is at a premium and/or when it is not needed so often.
function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
while((c=c.parentNode)&&c!==p);
return !!c;
}
..or as one-liner (just 64 chars!):
function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}
and jsfiddle here.
Usage:
childOf(child, parent) returns boolean true|false.
Explanation:
while evaluates as long as the while-condition evaluates to true.
The && (AND) operator returns this boolean true/false after evaluating the left-hand side and the right-hand side, but only if the left-hand side was true (left-hand && right-hand).
The left-hand side (of &&) is: (c=c.parentNode).
This will first assign the parentNode of c to c and then the AND operator will evaluate the resulting c as a boolean.
Since parentNode returns null if there is no parent left and null is converted to false, the while-loop will correctly stop when there are no more parents.
The right-hand side (of &&) is: c!==p.
The !== comparison operator is 'not exactly equal to'. So if the child's parent isn't the parent (you specified) it evaluates to true, but if the child's parent is the parent then it evaluates to false.
So if c!==p evaluates to false, then the && operator returns false as the while-condition and the while-loop stops. (Note there is no need for a while-body and the closing ; semicolon is required.)
So when the while-loop ends, c is either a node (not null) when it found a parent OR it is null (when the loop ran through to the end without finding a match).
Thus we simply return that fact (converted as boolean value, instead of the node) with: return !!c;: the ! (NOT operator) inverts a boolean value (true becomes false and vice-versa).
!c converts c (node or null) to a boolean before it can invert that value. So adding a second ! (!!c) converts this false back to true (which is why a double !! is often used to 'convert anything to boolean').
Extra:
The function's body/payload is so small that, depending on case (like when it is not used often and appears just once in the code), one could even omit the function (wrapping) and just use the while-loop:
var a=document.getElementById('child'),
b=document.getElementById('parent'),
c;
c=a; while((c=c.parentNode)&&c!==b); //c=!!c;
if(!!c){ //`if(c)` if `c=!!c;` was used after while-loop above
//do stuff
}
instead of:
var a=document.getElementById('child'),
b=document.getElementById('parent'),
c;
function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}
c=childOf(a, b);
if(c){
//do stuff
}
Another solution that wasn't mentioned:
Example Here
var parent = document.querySelector('.parent');
if (parent.querySelector('.child') !== null) {
// .. it's a child
}
It doesn't matter whether the element is a direct child, it will work at any depth.
Alternatively, using the .contains() method:
Example Here
var parent = document.querySelector('.parent'),
child = document.querySelector('.child');
if (parent.contains(child)) {
// .. it's a child
}
You can use the contains method
var result = parent.contains(child);
or you can try to use compareDocumentPosition()
var result = nodeA.compareDocumentPosition(nodeB);
The last one is more powerful: it return a bitmask as result.
Take a look at Node#compareDocumentPosition.
function isDescendant(ancestor,descendant){
return ancestor.compareDocumentPosition(descendant) &
Node.DOCUMENT_POSITION_CONTAINS;
}
function isAncestor(descendant,ancestor){
return descendant.compareDocumentPosition(ancestor) &
Node.DOCUMENT_POSITION_CONTAINED_BY;
}
Other relationships include DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDING, and DOCUMENT_POSITION_FOLLOWING.
Not supported in IE<=8.
I came across a wonderful piece of code to check whether or not an element is a child of another element. I have to use this because IE doesn't support the .contains element method. Hope this will help others as well.
Below is the function:
function isChildOf(childObject, containerObject) {
var returnValue = false;
var currentObject;
if (typeof containerObject === 'string') {
containerObject = document.getElementById(containerObject);
}
if (typeof childObject === 'string') {
childObject = document.getElementById(childObject);
}
currentObject = childObject.parentNode;
while (currentObject !== undefined) {
if (currentObject === document.body) {
break;
}
if (currentObject.id == containerObject.id) {
returnValue = true;
break;
}
// Move up the hierarchy
currentObject = currentObject.parentNode;
}
return returnValue;
}
Consider using closest('.selector')
It returns null if neither element nor any of its ancestors matches the selector. Alternatively returns the element which was found
try this one:
x = document.getElementById("td35");
if (x.childElementCount > 0) {
x = document.getElementById("LastRow");
x.style.display = "block";
}
else {
x = document.getElementById("LastRow");
x.style.display = "none";
}
TL;DR: a library
I advise using something like dom-helpers, written by the react team as a regular JS lib.
In their contains implementation you will see a Node#contains based implementation with a Node#compareDocumentPosition fallback.
Support for very old browsers e.g. IE <9 would not be given, which I find acceptable.
This answer incorporates the above ones, however I would advise against looping yourself.
I have a JSON object which I get from a server. The key which I get the value from looks something like this:
var myJson = data.body.region.store.customer.name;
I am trying to get the name key here, but sometimes the JSON (which comes from a service I have no control over) will have some empty fields, like for instance name might not be defined so the object will actually look like this: data.body.region.store.customer. Sometimes too customer, or store, or region might not be defined (If the data doesn't exist the service doesn't return a null or empty string for the value).
So if I need the name what I am doing is this:
if(data.body.region.store.customer.name){
//Do something with the name
}
But say even store isn't defined, it will not get the value for name(which I would expect to be undefined since it doesn't exist) and the program crashes. So what I am doing now is checking every part of the JSON before I get the value with AND operands:
if(data && data.body && data.body.region && data.body.region.store && data.body.region.store.customer && data.body.region.store.customer.name){
//Do something with the name value then
}
This works, because it checks sequentially, so it first checks does data exist and if it does it checks next if data.body exists and so on. This is a lot of conditions to check every time, especially since I use the service a lot for many other things and they need their own conditions too. So to just check if the name exists I need to execute 6 conditions which of course doesn't seem very good performance wise (and overall coding wise). I was wondering if there is a simpler way to do this?
var myJson = null;
try {
myJson = data.body.region.store.customer.name;
}
catch(err) {
//display error message
}
You can try following
function test(obj, prop) {
var parts = prop.split('.');
for(var i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
if(obj !== null && typeof obj === "object" && part in obj) {
obj = obj[part];
}
else {
return false;
}
}
return true;
}
test(myJson, 'data.body.region.store.customer.name');
I faced a very interesting issue today which many of you might find elementary but since I am just learning to use jQuery, I am interested to know how this works.
I have two arrays and I'm iterating through the elements of array. One array is arrAllDetailsConstantData and the other one is arrAllDetails. I want to compare using arrAllDetailsConstantData and update using arrAllDetails.
I am using nested loops. But what is happening is that while updating array arrAllDetails, array arrAllDetailsConstantData is also getting updated. I assume this is somehow related to scope of parent this (though I am just guessing). Can you please help me with this?
Here is my code:
$.each(privateVariables.arrAllDetailsConstantData, function() {
if (this.AssociationId == value && this.uniqueChargeAttr == uniqueChargeAttr) {
if (this.Units == $("#txtUnits").val() &&
this.Modifier1 == $("#txtModifier1").val() &&
this.Modifier2 == $("#txtModifier2").val() &&
this.Modifier3 == $("#txtModifier3").val() &&
this.Modifier4 == $("#txtModifier4").val() &&
this.DxOption == $("#ddlDxOption").val() &&
this.DxCode1 == $("#txtDx1").val() &&
this.DxCode2 == $("#txtDx2").val() &&
this.DxCode3 == $("#txtDx3").val() &&
this.DxCode4 == $("#txtDx4").val()) {
} else {
$.each(arrAllDetails, function() {
if (this.AssociationId == value && this.uniqueChargeAttr == uniqueChargeAttr) {
this.ActionType = "M";
this.CptName = $("#lblCptDesc").text();
this.CptDesc = $("#lblCptDesc").text();
this.Units = $("#txtUnits").val();
this.Modifier1 = $("#txtModifier1").val();
this.Modifier2 = $("#txtModifier2").val();
this.Modifier3 = $("#txtModifier3").val();
this.Modifier4 = $("#txtModifier4").val();
this.DxOption = $("#ddlDxOption").val();
this.DxCode1 = $("#txtDx1").val();
this.DxCode2 = $("#txtDx2").val();
this.DxCode3 = $("#txtDx3").val();
this.DxCode4 = $("#txtDx4").val();
privateVariables.arrActionData.push(this);
}
});
}
}
});
// test code ends
First, the jQuery's this refers to the calling element.
In a $.each, this refers to the iterated element.
So, in your first loop : $.each(privateVariables.arrAllDetailsConstantData, function() {});, this will be the currently looping element of your arrAllDetailsConstantData array.
In the second loop, this will be the currently looping element of your arrAllDetails array.
Secondly, we need the arrays creation's code.
Don't forget, in many languages, that if you use this arrAllDetailsConstantData = arrAllDetails, the pointer reference to those two object are pointing to the same memory range.
In this case, use arrAllDetailsConstantData = arrAllDetails.slice(); from Array.prototype.slice.
for the scope of "this" you can check out JQuery and 'this' object
As for your code: You need to add how you created the two arrays in the first place.
Based on your code they seem to be created by using the same elements (so arrDetails seems to be a subset of the objects in arrAllDetails). Something in the way of
$.each(arrDetails, function() {arrAllDetails.push(this); });
or by simply assinging:
arrDetails = arrAllDetails;
If that is the case then the object within the arrays are actually the SAME. So editing the element in one array will also edit it in the other array (they point to the same object in memory).
I am not sure what you try to accomplish, but if you want the objects to be different you need to clone the entries so they are seperate objects instead of just using them.
Apart from that you basic understanding from this (it applies to the closest scope) is correct, but overridden by other issues.
In Javascript this refers to the owner of a function-execution. It is not always easy to know what the owner actually is. With scoping on HTML-Elements, this usually refers to the elements itself. Inside an iteration (like $.each(data, function)) this refers to the item, that is iterated over.
You use this in 2 different contexts. Each time in another iteration.
$.each(privateVariables.arrAllDetailsConstantData, function() {
if (this.AssociationId == value && this.uniqueChargeAttr == uniqueChargeAttr) {
//in this context: "this" refers to the currently selected
//item of the iteration over:
// privateVariables.arrAllDetailsConstantData
} else {
$.each(arrAllDetails, function() {
// in this context: "this" refers to the selected item
// of the iteration over "arrAllDetails"
});
}
});
You can preserve the context when entering an iteration by binding this to a local variable.
$.each(privateVariables.arrAllDetailsConstantData, function() {
if (this.AssociationId == value && this.uniqueChargeAttr == uniqueChargeAttr) {
// this refers to an item from privateVariables.arrAllDetailsConstantData
} else {
var context = this;
$.each(arrAllDetails, function() {
// context refers to an item from privateVariables.arrAllDetailsConstantData
// so: use
context.AssociationId
// instead of
this.AssociationId
});
}
});
I don't have much experience in JavaScript, so far I have this:
function loop() {
var authorDivs = document.getElementById('ctl00_MainContent_MCPObjectInfo_dvCreatorView').getElementsByTagName("div");
for (var i = 0; i < authorDivs.length; i++) {
var divOfDiv = authorDivs[i].getElementsByTagName("div");
if (typeof divOfDiv.item(i) === 'undefined' || divOfDiv.item(i) === null) {
console.log("This is undefined or null");
}
else {
var realDivs = divOfDiv.item(i);
realDivs.item(i).textContent = "please work plz";
}
}
}
I get the following error from the console in FireFox: TypeError: realDivs is undefined on this line: realDivs.item(i).innerHTML = "please work plz";
Essentially what I have (in my mind) is a loop that goes through authorDivs and gets all of the divs within those divs and saves them in divOfDiv. I then check to see if the divs in divOfDiv are undefined or null, if they are not then those divs get saved in a variable realDivs which I then use to edit the innerHTML. That's what I'd ideally like to see happen, what is causing the error? What am I doing wrong?
Note: I do not have access to jQuery but only JavaScript.
Edit: I've added the changes suggested below and its fixed that -- thanks! But I'm now getting the following error: TypeError: realDivs.item is not a function
What is causing that? And on another note how do I know when I'm dealing with an array and when I'm dealing with an HTMLCollection? Do you just assume? I've never used a loosely typed language before so its new to me.
Well, you'll need to move that code inside the conditional block that is supposed to prevent it! Also, || "null" is not going to work as you expect, you'll need to check for || divOfDiv.item(i) === null explicitly.
So try
for (var i = 0; i < authorDivs.length; i++) {
var divOfDiv = authorDivs[i].getElementsByTagName("div");
if (divOfDiv.item(i) == null) {
console.log("This is undefined or null");
} else {
var realDivs = divOfDiv.item(i)
realDivs.item(i).innerHTML = "please work plz";
console.log(divOfDiv.item(i));
}
}
However, that still doesn't really work for two reasons:
The i index you use to access the i-th divOfDiv comes from the iteration over authorDivs - hardly what you want. Instead, use a second loop over all divOfDivs.
Your realDivs variable does hold a single <div>, which does not have an .item method. You'd just directly access its .innerHTML property.
So you should use
var authorDivs = document.getElementById('authorView').getElementsByTagName("div");
for (var i=0; i<authorDivs.length; i++) {
var divsOfDiv = authorDivs.item(i).getElementsByTagName("div");
for (var j=0; j<divsOfDiv.length; j++) {
var realDiv = divsOfDiv.item(j);
realDiv.innerHTML = "please work plz";
console.log(realDiv);
}
}
it will happen in case when your if (typeof divOfDiv.item(i) === 'undefined' || 'null') returns true. Then you never initialize realDivs (what would happen if condition was falsy). Later you try to call item function on that unitialized object
There are two problems in the code.
comparing DOM object with 'undefined' and null. If div tag is not available in authorDivs[i], it will return empty DOM array. So, comparision of empty DOM array with undefined and null is not good approach. We can use array length property for doing validation.
divOfDiv = authorDivs[i].getElementsByTagName("div");
if(divOfDiv.length > 0) { console statement}
As item(i) is already return single DOM element, item(i) of "realDivs" variable is not proper approach. In addition to this, innerHTML method needs to be used after validating whether realDivs contains DOM element. Please update the code as below.
var realDivs = divOfDiv.item(i);
realDivs ? (realDivs.innerHTML = "please work plz"): null;
Note : item(i) will return null if DOM is not available.
I'm writing a script that adds labels to things on a page using an element from an array based on part of the link... so my array looks like this:
var componentList[9] = "Sunnyseed"
var componentList[10] = "Echoberry"
var componentList[11] = "Riverstone"
var componentList[13] = "Auraglass"
var componentList[14] = "Skypollen"
You'll notice there is no '12'... I want the label to be 'Unknown' when the array item doesn't exist. Now, I can't exactly test my solution since I can't cause the target page to throw me a 12... so I was hoping somebody would tell me whether this will do what I want or not...
var component = ""
if(typeof componentList[critterIDval] == 'undefined'){
component="Unknown"
}
else{
component=componentList[critterIDval]
}
This is obviously not the full script, but it should be the important stuff... I just want to know if that will make it say 'Unknown' when the critterIDval is 12 - since it could take years to come across the situation for testing.
You're pretty much there. You're using a single-equals sign in your comparison, so that will mess it up, and I'm not sure you can create a JS array like that, but aside from that, you're good.
Here is the test I ran for it:
var componentList = [];
componentList[9] = "Sunnyseed";
componentList[10] = "Echoberry";
componentList[11] = "Riverstone";
componentList[13] = "Auraglass";
componentList[14] = "Skypollen";
for (var critterIDval = 9; critterIDval < 15; critterIDval++) {
if (typeof componentList[critterIDval] == 'undefined') { // double equals here
component = "Unknown";
} else {
component = componentList[critterIDval];
}
console.log(component);
}
It looks fine.
Though if you are sure that the value will never be an empty string(like componentList[14] = '';) then you can try
var component = componentList[critterIDval] || 'Unknown'
I want the label to be 'Unknown' when the array item doesn't exist.
The typeof operator does not tell you if a property exists or not as it returns undefined when the property doesn't exist but also when it does exist and has been assigned a the value undefined or simply created but hasn't been assigned a value.
There are two primary ways to test for the existence of a property: the in operator, which also looks on the [[Prototype]] chain and the hasOwnProperty method of all Objects. So
if (componentList.hasOwnProperty(critterIDval)) {
component = "Unknown"
} else {
component = componentList[critterIDval]
}
which you could also write as:
component = componentList.hasOwnProperty(critterIDval)? componentList[critterIDval] : 'unknown';
PS. there are other methods, such as looking at Object.keys(componentList) and componentList.propertyIsEnumerable(critterIDval), but the above are the most common.
Edit
If your requirement is not just to test for property existence but to also test for a "truthy" value, then:
if (componentList[critterIDval])
may be sufficient and will return false where the value is '' (empty string), 0, false, NaN, undefined or null.
Maybe just testing for a non–empty string or number will do:
if (/.+/.test(componentList[critterIDval]))
but that returns true for NaN, null and so on. So you need to specify what you are actually testing for, otherwise you may get undesired results for some values.