scope of "this" in jQuery function - javascript

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
});
}
});

Related

Javascript delete does not removed the desired property

I am using nodejs to create a game. It happens that this time I am not able to use Delete. It is not working and I have no idea why.
I execute this line:
delete Bullet.list[i]
And Bullet.list is an object with properties and after executing this line, the desired property is not removed.
Here is my code (you can see the above line of code in context here below):
var Map = require("./Map.js")
var Player = require("./Player.js");
var Bullet = require("./Bullet.js");
var Fly = require("./Fly.js");
var Settings = require("./Settings.js");
var Simulation = function(SOCKET_LIST){
//Update Bullets
for(var i in Bullet.list){
var bullet = Bullet.list[i];
bullet.update();
var shooter = Player.list[bullet.parent];
//Bullets collide with flies
for(var i in Fly.list){
var fly = Fly.list[i];
if(!bullet.toRemove && bullet.getDistance(fly) < 15){
if(shooter){
shooter.updateCoins(fly.killCoins);
shooter.updateXp(fly.killXp);
}
bullet.toRemove = true;
fly.toRemove = true;
}
}
//Collide with player
for(var i in Player.list){
var player = Player.list[i];
if(player.death) continue;
if(!bullet.toRemove && bullet.getDistance(player) < 32 && shooter !== player.id && shooter.team != player.team && !player.immune){
player.hp -= 1;
player.hpChanged = true;
if(player.hp <= 0){
if(shooter)
player.kill(shooter);
else
player.kill();
}
bullet.toRemove = true;
}
}
//Collide with map
if(typeof shooter == "undefined" || shooter.shootsCollideMap && Map.isColliding(bullet))
bullet.toRemove = true;
//Remove bullets
if(bullet.toRemove){
delete Bullet.list[i]; /*HERE IS THE PROBLEM. THE PROPERTY IS NOT BEING DELETED*/
Bullet.removePack.push[bullet.id];
}
}
I am not able to delete the property "i" from the object Bullet.list.
Your three for loops are all sharing the same variable i and thus you aren't deleting the i index you want to. This is because var i is function scoped, not scoped to the individual for loop.
When you do this:
delete Bullet.list[i];
The value of i is whatever i is after your previous for loop and probably not the item you actually want to delete. Perhaps you mean to break out of the for loop previously so that i will be a specific item you want to remove?
Or you need to separately save to another variable an index of an item that you want to remove. Or, just remove an item inside the for loop when the value of i is current.
Remember that var is function scoped. So both your var i declarations in your two nested for loops are actually referencing the exact same variable, not declaring a new one. I would recommend changing the name of the loop index in one of the two loops to be a separate variable.
If you are running in an environment where let is fully supported, you could use let i instead of var i and then the value of i would be uniquely scoped to only the for loop in which it was declared.
When you delete an array element, the array length is not affected. This holds even if you delete the last element of the array.
You could try Bullet.list.splice(i, 1) as an alternative to delete.
There is a lot going on in this code,, hope this helps!

Check if elements are part of wrapper [duplicate]

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.

Cycling through jquery object to get its values

I have to loop through the object and get all its values. Here's an image of object
So, basically i have to get classificatorCodeId and loop through observationList to get its child classificatorCodeId's and observationLists, and so on.
If you guys have any ideas, what's the best way to loop through this object, id be happy to try your solutions.
Following code will print all values in that object as key --> value pairs
var traverse = function(mainObject)
{
$.each(mainObject, function(index, subObject)
{
if(($.type(subObject) === "object" || $.type(subObject) === "array"))
{
traverse(subObject);
}
else
{
console.log(index+" --> "+subObject);
}
});
}
traverse(mainObject);

JS: Prevent Error if Accessing Attributes of Undefined Object

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]) }

Build a switch based on array

I want to create a Javascript switch based on an array I'm creating from a query string. I'm not sure how to proceed.
Let's say I have an array like this :
var myArray = ("#general","#controlpanel","#database");
I want to create this...
switch(target){
case "#general":
$("#general").show();
$("#controlpanel, #database").hide();
break;
case "#controlpanel":
$("#controlpanel").show();
$("#general, #database").hide();
break;
case "#database":
$("#database").show();
$("#general, #controlpanel").hide();
break;
}
myArray could contain any amount of elements so I want the switch to be created dynamically based on length of the array. The default case would always be the first option.
The array is created from a location.href with a regex to extract only what I need.
Thanks alot!
#Michael has the correct general answer, but here's a far simpler way to accomplish the same goal:
// Once, at startup
var $items = $("#general,#controlpanel,#database");
// When it's time to show a target
$items.hide(); // Hide 'em all, even the one to show
$(target).show(); // OK, now show just that one
If you really only have an array of selectors then you can create a jQuery collection of them via:
var items = ["#general","#controlpanel","#database"];
var $items = $(items.join(','));
Oh, and "Thanks, Alot!" :)
I think you want an object. Just define keys with the names of your elements to match, and functions as the values. e.g.
var switchObj = {
"#general": function () {
$("#general").show();
$("#controlpanel, #database").hide();
},
"#controlpanel": function () {
$("#controlpanel").show();
$("#general, #database").hide();
},
"#database": function () {
$("#database").show();
$("#general, #controlpanel").hide();
}
}
Then you can just call the one you want with
switchObj[target]();
Granted: this solution is better if you need to do explicitly different things with each element, and unlike the other answers it focused on what the explicit subject of the question was, rather than what the OP was trying to accomplish with said data structure.
Rather than a switch, you need two statements: first, to show the selected target, and second to hide all others.
// Array as a jQuery object instead of a regular array of strings
var myArray = $("#general,#controlpanel,#database");
$(target).show();
// Loop over jQuery list and unless the id of the current
// list node matches the value of target, hide it.
myArray.each(function() {
// Test if the current node's doesn't matche #target
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
});
In fact, the first statement can be incorporated into the loop.
var myArray = $("#general,#controlpanel,#database");
myArray.each(function() {
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
else {
$(this).show();
}
});
Perhaps you're looking for something like this? Populate myArray with the elements you're using.
var myArray = ["#general","#controlpanel","#database"];
var clone = myArray.slice(0); // Clone the array
var test;
if ((test = clone.indexOf(target)) !== -1) {
$(target).show();
clone.splice(test,1); // Remove the one we've picked up
$(clone.join(',')).hide(); // Hide the remaining array elements
}
here you dont need to explicitly list all the cases, just let the array define them. make sure though, that target exists in the array, otherwise you'll need an if statement.
var target = "#controlpanel";
var items = ["#general","#controlpanel","#database"];
items.splice($.inArray(target, items), 1);
$(target).show();
$(items.join(",")).hide();
items.push(target);

Categories

Resources