JavaScript - extremely confused on removing elements from Container - javascript

I'm making a 2D, top-down Zelda style web rpg single player in JavaScript.
When the player (purple shirt) walks near a cat, it will "rescue" it... which basically removes the animalContainer from the ContainerOfAnimals (thereby removing animalContainer's BMP from the stage), and then add the id of that animalContainer to a rescuedTotal_Array...
The weird thing is, In the pic below, I'm able to rescue animalContainer2 and then rescue animalContainer1... But if I go from animalContainer1 to animalContainer2, it throws a
Uncaught TypeError: Cannot read property 'id' of undefined` error.
...
Basically:
Working way: get ID 22, then up to ID 21 ->>> One thing I notice with this is that the element name doesn't change for the ID... not sure why... so for the broken way... it may not be able to even associate element name animalContainer2 with an ID? But I don't know how the ID and the name could get disassociated like that..
ID in rescued array...: 22, element name: animalContainer1, rescued array length: 2, elem index pos: 0, index: 0
ID in rescued array...: 21, element name: animalContainer1, rescued array length: 2, elem index pos: 1, index: 0
Broken way- throws error: get ID 21, then down to ID 22
ID in rescued array...: 21, element name: animalContainer1, rescued array length: 1, elem index pos: 0, index: 0
1. Uncaught TypeError: Cannot read property 'id' of undefined
Code Snippet:
function grabIt(NPC_id, index) {
//check if NPCid already exists in array before adding...
if ($.inArray(ContainerOfAnimals.children[index].id, rescuedTotal_Array) == -1) {
rescuedTotal_Array.push(ContainerOfAnimals.children[index].id);
}
if (rescuedTotal_Array.length == 2) {
for (var z = 0; z < rescuedTotal_Array.length; z++) {
console.log("ID in rescued array...: " + rescuedTotal_Array[z] + ", \n element name: " + ContainerOfAnimals.children[index].name + ", rescued array length: " + rescuedTotal_Array.length + ",\n elem index pos: " + z + ",\n index: " + index);
}
}
//when I remove the first added element to rescuedTotalArray... the 2nd element's index assumes first added element's index... (goes from 1 to 0)
console.log(index);
console.log("element removed: " + ContainerOfAnimals.children[index]);
stage.update();
ContainerOfAnimals.removeChild(ContainerOfAnimals.children[index]);
updateHUD();
}
I have no idea why the order in which I store elements in the array/remove them from the stage would matter...
EDIT: I feel I can solve this issue by removing element from ContainerOfAnimals by element ID instead of by index... by Container object offers no getChildID() function...
so I tried:
var childToRemove = document.getElementById(ContainerOfAnimals.children[index]);
console.log(childToRemove);
ContainerOfAnimals.removeChild(childToRemove);
console.log(childToRemove) gives me null for children
But doing this: console.log(ContainerOfAnimals.children[index].id); gives me id 21, which is the correct id...

Beware that EaselJS elements aren't DOM elements.
Supposing you want to remove an element by its id, I'd suggest this :
function removeElementById(container, id) {
for (var i=container.getNumChildren(); i-->0;) {
if (container.getChildAt(i).id===id) {
container.removeChildAt(i);
return true;
}
}
return false;
}
But using the id might not be the best architectural solution. In my EaselJS program I keep a reference to the objects themselves so that I don't have to search them.

It may be possible that your rescuedTotal_Array is an associative Array (or was morphed to one) and could contain something like:
rescuedTotal_Array = [1:object,2:object,object:object]
The array above has the length of 3. But you cannot access the third element via index 2, because its index is some kind of object.
Try to dump the content of your rescuedTotal_Array before accessing it (so you can see if everything is ok). This is not a solution, but it may help you so that you can find the error by yourself. Use something like
for (index in rescuedTotal_Array) {
if (rescuedTotal_Array.hasOwnProperty(index)) {
console.log(rescuedTotal_Array[index]);
}
else {
console.log("no entry found for index: "+index);
}
}

I am not entirely sure whatever if this is the case (I can't know it without taking a look at your whole program), but it could be this:
I assume that your "ContainerOfAnimals" is a HTML element and that you are retrieving it's child nodes using it's .children -property. The thing is that .children -property returns a "live list"!
Let me demonstrate:
var children = ContainerOfAnimals.children;
console.log(children.length) // 2;
ContainerOfAnimals.removeChild(children[0]);
console.log(children.length) // 1;
Let's say that you remove the very first child of ContainerOfAnimals. What happens? The children -list has now changed so that the second element becomes the first element, third element becomes the second element etc...
You could fix this by using a static array containing all the children and like this:
var children = [].slice.call(ContainerOfAnimals.children);
console.log(children.length) // 2;
ContainerOfAnimals.removeChild(children[0]);
console.log(children.length) // 2;
Now removing the element from the DOM -tree does not remove the element from the children array (which is static). Remember, that [].slice.call does not work in IE8 or lower.
Let me know if this was the problem. I have not enough points to comment, so I had to make a full post :)

HY,
Try for statement like this:
for(var z = rescuedTotal_Array.length; z >= 0; z-- )
If you noticed I just turned it around. It starts at the and going backwards.
It is stupid but, maybe this error is just that :)
Let me know how it goes!
Regards

Related

How to check if an object inside an array is still there

So I'm working on a project where objects are stored in an array. People can remove objects manually, but now I want to make so when an object is created it will be removed from the array after 5 seconds, only if it's still there (not manually removed). I'm using a settimeout where the object is spliced. How to do this that it will only remove the object if it's still there?
Edit:
So I wasn't very clear, when the timeout removes an object and it's not there anymore it just removes the next one like you'd expect.
Here's my code for remove the object manually:
if (intersect(player, food)) {
foods.splice(i, 1);
}
Here's the code of creating and removing with a timer:
food = {
x: x,
y: y,
};
foods.push(foods[i]);
setTimeout(function(){
foods.splice(i, 1);
}, 10000);
You have to check the index of the object in the array..
Array.indexOf() // returns -1 if its not there
function addToArrayAndRemoveAfter(object, array, removeAfterMs) {
array.push(object)
// remove after ms
setTimeout(() => {
var idx = array.indexOf(object);
idx > -1 && array.splice(idx, 1)
}, removeAfterMs)
}
Please add your code next time your asking something:
Its almost much harder to parse code out of human written text, then vice versa

can't get nested forEach to work in Javascript

So I guess the title is selfexplanatory. I have some code with nested forEach loops inside it. The loops are iterating over an array of chapter objects. Each object can have multiple child nodes and they again can have multiple child nodes, and so on.
I want to end up with one array which contains nested arrays with the child nodes.
So far my code looks like this:
exports.chapter = function(req, res) {
var chapters = [],
result = [];
chapters = exports.index(req, res);
chapters.forEach(function(chapter) {
if(chapter.orphan){
result.add({
'chapter': chapter,
'children': getChildren(chapter.children)
});
}
});
function getChildren(siblings) {
var children = [];
chapters.forEach(function(chapter) {
if($.inArray(chapter, siblings)){
children.add({
'chapter': chapter,
'children': getChildren(chapter.children)
});
}
});
return children;
};
};
I don't get any errors except for my page not loading. It doesn't write anything in my console. I think it's a problem in the setup but I'm unable to find out where at the moment. Really hope you guys can help.
Most likely problem is here:
if($.inArray(chapter, siblings)){
$.inArray is a horribly misnamed method: It returns an index, or -1 if not found, not a flag as the name implies. -1 is, of course, truthy; and 0 (a valid index), is falsey, so your if probably wants to be
if($.inArray(chapter, siblings) != -1){
// We found it...
}
or possibly
if($.inArray(chapter, siblings) == -1){
// We didn't find it
}
It's a bit strange.. I don't understand why you're using 'add' instead of 'push' method. If I try to "add" an object to an array I get an usual error. Don't you?

Pushing data into an array of objects

Alright, I've got this blank array of objects.
I am dynamically finding every node in a web page and each node is going to have it's own object and properties.
I need a way to throw the values I need into their respective objects property
So, for example, I find the body node. I now have a special little object for this node. I need to throw pretty much everything about this little guy into his object's properties.
So I pretty much need it to render like this:
Turning this:
<html>
<body style="margin:0; padding:0;" title="My Title">
<p>some text</p>
<div class="wrapper"></div>
<footer></footer>
</body>
</html>
Into this:
this.nodesInfo = [ // All nodes in the page's DOM
{
type: 'body', // ex: body, section, aside, div, etc.
id: 'myID', // the Id of that element
class: ['myClass1', 'myClass2'], // the class/class' of that element
depth: '2', // the level in the page's DOM in which that element sits, this will be an integer
parent: 'html', // that elements direct parent Node
children:['div.wrapper', 'p', 'footer'], // any child Nodes that, that element may be a parent to
text: '', // the text inside that element if any exists
attributes: ["style=margin:0; padding:0;", "title='My Title'"] // all attributes of this node
}
];
It would of course cycle through each node it discovered and do this for each node accordingly, until it ran out of nodes.
The class, children, and attributes properties are arrays for the simple possibility of multiples of any of these. Everything else is just a string since a node can't have more than one ID, title, or direct parent tag.
If a node does not contain some of these properties then that property would remain blank/null/undefined.
My question is simple. Is this possible, if not would I instead have to create each object individually and the push them into my nodesInfo array?
I think the easiest way to go about this would be making an object of each Node and then pushing them all (once they are all created) into an array.
I was building something like this the other night. This should work and you can add more stuff easily. http://jsfiddle.net/elclanrs/UHbMa/
$.fn.buildTree = function() {
var tree = {};
this.find('*').andSelf().each(function(i, v) {
var parents = $(this).parents().length - 1,
depth = 0;
while (depth < parents) {
depth++;
}
tree[v.tagName.toLowerCase() + '(' + i + ')'] = {
id: (v.id) ? '#' + v.id : '',
className: (v.className) ? '.' + v.className.replace(' ', '.') : '',
depth: depth
};
});
return tree;
};
// Then you can do this...
var tree = $('#element').buildTree();
for (var tag in tree) {
// Get your variables
var tag.match(/\w+/), // Get rid of `(n)`
id = tree[tag].id,
className = tree[tag].className,
depth = tree[tag].depth;
html = 'something';
// Bla bla
}

javascript compare objects in array

I have a grid of pictures on a page. And, periodically, I want to randomly swap one out for one of 50 I have in an array of Objects- but only if they're not already in the grid. This last part is what my code is failing to do.
I first get all 50 items, and put them into an allmedia array:
// initialize
var allmedia = getAllMedia();
var imagesInGrid = [];
When I place the items in the grid, I add to an array of grid items:
imagesInGrid.push(allmedia [i]); // while looping to fill DOM grid
Then, every 8 seconds I run a getRandomImage() routine that randomly gets an image from the allmedia array and then tests it to see if it's not already in the DOM.
function getRandomImageNotInGrid(){
var randomNumber = Math.floor(Math.random() * allmedia.length);
if (!isInArray(allmedia[randomNumber], imagesInGrid)) {
return allmedia[randomNumber];
} else {
return getRandomImageNotInGrid();
}
}
function isInArray(item, arr) {
if(arr[0]===undefined) return false;
for (var i=arr.length;i--;) {
if (arr[i]===item) {
return true;
}
}
return false;
}
But when I step through the code the (arr[i]===item) test is failing. I can see that the two objects are exactly the same, but the === isn't seeing this as true.
Is this a ByReference / ByValue issue? What am I doing wrong?
console.log:
arr[i]===item
false
arr[i]==item
false
typeof item
"object"
typeof arr[i]
"object"
Edit::
In the output below, I don't understand why arr[0] is not the same as 'item'. I use the exact same object that I put into allmedia as I do when I place the item into the page and, accordingly update imagesInGrid.
console.dir(arr[0]);
Object
caption: Object
comments: Object
created_time: "1305132396"
filter: "Poprocket"
id: "69204668"
images: Object
likes: Object
link: "http://instagr.am/p/EH_q8/"
location: Object
tags: Array[2]
type: "image"
user: Object
__proto__: Object
console.dir(item);
Object
caption: Object
comments: Object
created_time: "1305132396"
filter: "Poprocket"
id: "69204668"
images: Object
likes: Object
link: "http://instagr.am/p/EH_q8/"
location: Object
tags: Array[2]
type: "image"
user: Object
__proto__: Object
Instead of randomly selecting one from allmedia, can you instead remove one from allmedia?
var randomNumber = Math.floor(Math.random() * allmedia.length);
imagesInGrid.push(allmedia.splice(randomNumber,1));
When you use ===, you are comparing the objects by reference. What type of objects are you using to compare? Are you sure they are the same object reference? For example, if you are using strings, you may want to use == instead of ===. If you are using DOM objects, you will want to compare the source, as Alxandr suggested.
Also, your for loop syntax appears to be wrong:
for (var i=arr.length;i--;)
Should be:
for (var i=arr.length - 1; i >= 0; i--)
...if I'm not mistaken.
You don't show any code for how the image "objects" are created or how they are added and removed from the DOM. If you are creating image elements and storing references in an array, then replacing the DOM element with the one from the array, then the comparision should work.
However, if your image object is a bundle of data that is used to create an image element for display, then they will never be equal to each other. Every javascript object is unique, it will only ever be equal to itself.
But I suspect the simplest solution is that suggested by ic3b3rg - remove the object from allMedia when you select it, that way you don't have to test if it's already in imagesInGrid because you can only select each image once. If you want the display to go on forever, then when allmedia is empty, put all the images from imagesInGrid back into it and start again.
Edit
Your problem is the for loop. When you set :
for (var i=arr.length;i--;) {
// On first iteration, i=arr.length
// so arr[i] is undefined
}
i is not decremented until after the first loop, so set i=arr.length-1. It is more common to use while with a decrementing counter:
var i = arr.length;
while (i--) {
// i is decremented after the test
}

Javascript: How do I splice a value from an array with an index of 0?

I am attempting to remove a value from an array using splice. starting at 0 and ending at 0 splice, but it is not removing the value at index 0. I added a function getItemRow to check the species index which returns 0. I dumped the values of the array into an alert and it still outputs species which should of been deleted. invalidElement.splice(indexValue, indexValue); works as expected for indexes that are NOT 0. Why is this happening and how do I delete the value that has 0 index?
javascript code:
var invalidElement = new Array("species", "alias", "gender", "breeding", "birth_date");
//This function will be removed once fixed!!
function getItemRow()
{
var myPosition=-1
for (i=0;i<invalidElement.length;i++)
{
if(invalidElement[i]=="species") {
myPosition = i;
break;
}
}
alert(myPosition)
}
function validateElement(formId, element, selector, errorContainer)
{
getItemRow()//for testing purposes
//var indexValue = $.inArray(element, invalidElement);
var indexValue = invalidElement.indexOf(element);
alert(element);
$.ajax({
type: 'POST',
cache: false,
url: "validate_livestock/validate_form/field/" + element,
data: element+"="+$(selector).val(),
context: document.body,
dataType: 'html',
success: function(data){
if (data == "false")
{
$(errorContainer).removeClass('element_valid').addClass('element_error');
invalidElement = element;
alert(invalidElement.join('\n'))//for testing purposes
//alert(indexValue);
}
else
{
$(errorContainer).removeClass('element_error').addClass('element_valid');
invalidElement.splice(indexValue, indexValue);
alert(invalidElement.length);//for testing purposes
alert(invalidElement.join('\n'))//for testing purposes
}
}
});
}
$("#species").change(function(){
validateElement('#add_livestock', 'species', '#species', '.species_error_1')
});
I think you want splice(0, 1).
The second argument is how many you want removed...
An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed.
Source.
Splice can work in two modes; to remove or insert items.
When removing items you'll specify two parameters: splice(index, length) where index is the starting index, and length is a positive number of elements to remove (fyi: passing a "0", as in your example, does nothing--it's saying "remove zero items starting at index"). In your case you'll want:
invalidElement.splice(indexValue, 1); // Remove 1 element starting at indexValue
When inserting items you'll specify (at least) three parameters: splice(index, length, newElement, *additionalNewElements*). In this overload you normally pass 0 as a 2nd parameter, meaning to insert the new elements between existing elements.
var invalidElements = ["Invalid2", "Invalid3"];
invalidElements = invalidElements.splice(0, 0, "Invalid1");
There's also a convenience function for removing the first element in an array:
array.shift();
See: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/shift.
Mozilla Dev Center - Array.splice states that the second parameter is 'howMany' elements to remove.
I'm not sure how your code worked when it passed in the indexValue as the number of elements to remove, unless you were removing from the end of the array.

Categories