Straight forward: How do I change the position of Kinetic.Group children in the array? It is really important for me that I can alter the array and the position of each children.
I tried to add a child with Array.splice on a specific position but the library crashed. So I sticked to the native .add() function, took the child from the end of the array and spliced it on first place afterwards:
mapTilesContainer.add(image);
var tempChild = mapTilesContainer.children[mapTilesContainer.children.length - 1];
// delete it from the last place
mapTilesContainer.children.splice(mapTilesContainer.children.length - 1, 1);
// add it to the first place
mapTilesContainer.children.splice(0, 0, tempChild);
which somehow works, but if I then want to destroy a child regularly with .destroy() it crashes again:
mapTilesContainer.children[8].destroy();
Telling me: Uncaught TypeError: Cannot call method 'getLayer' of undefined in kinetic.js:2
Adding and destroying without messing with splice works though. Any ideas?
--- Very uncommon requirement ---
Is there no workaround in your code instead of messing with kinetic's internal arrays?
It's almost always a bad idea to mess with the internals of a library script
Having said that, I took a quick look at the kinetic's Container.js code.
https://github.com/ericdrowell/KineticJS/blob/master/src/Container.js
This is the internal add child method:
add: function(child) {
var children = this.children;
this._validateAdd(child);
child.index = children.length;
child.parent = this;
children.push(child);
this._fire('add', {
child: child
});
// chainable
return this;
},
Note that child.index property is set to the position of the child in the array of children.
Properly resetting each child.index is probably your key to manipulating Kinetic's children array.
At line 237 of Container.js is this internal method:
_setChildrenIndices: function() {
var children = this.children, len = children.length;
for(var n = 0; n < len; n++) {
children[n].index = n;
}
},
This method might be used by kinetic to reset the indices of a children array.
If you shuffle the children array and then execute your own version of _setChildrenIndex, you might not blow up your own app.
Again:
It's almost always a bad idea to mess with the internals of a library script
Related
I wrote a function to change the class of elements to change their properties. For some reason, only some of the elements have changed. It took me a few hours to find a solution, but it seems odd to me. Perhaps you can explain this to me.
This isn’t working:
function replace(){
var elements = document.getElementsByClassName('classOne');
for (var i = 0; i < elements.length; i++) {
elements[i].className = 'classTwo';
}
}
See the JSFiddle: only every second item is affected; only every second red element changes color to blue.
So I changed the final expression of the for loop to not increment i anymore:
function replace(){
var elements = document.getElementsByClassName('classOne');
for (var i = 0; i < elements.length; i) { // Here’s the difference
elements[i].className = 'classTwo';
}
}
This works well! It seems as though push is called and no increment is needed. Is this normal? It is different from the examples I’ve seen.
What's going on is an odd side effect. When you reassign className for each element of elements, the element gets removed from the array! (Actually, as # user2428118 points out, elements is an array-like object, not an array. See this thread for the difference.) This is because it no longer has the classOne class name. When your loop exits (in the second case), the elements array will be empty.
You could write your loop as:
while (elements.length) {
elements[0].className = 'classTwo'; // removes elements[0] from elements!
}
In your first case, by incrementing i, you are skipping half of the (original) elements that have class classOne.
Excellent question, by the way. Well-researched and clear.
getElementsByClassName returns a NodeList. A NodeList collection is a live collection, which means that the modification of the document affects the collection. more
Or revert the loop, beginning by length-1 and step down to 0
I have a Javascript Array that holds the contents of a page. In order to draw all the objects in the right places when the page loads I loop over the array and pull out the elements. This worked very well until I allowed the objects to have children within them.
The current array structure is
0-> {elements=[] frame={I keep the frame attributes here with key value pairs}}, 1-> {elements=[] frame={}}
However, I just started adding sub-elements to the elements array in each object. So now I have to loop through/draw each element, check to see if there are any children and if so then I have to draw them too.
The problem I'm having is that after I loop through the first 0 object and it's children the for loop stops running. Is it because I'm calling the same function multiple times? I've done that before so I don't think that's what is happening.
this.run_loop = function (spawn, dom) {
console.log(spawn)
alert(spawn.length)
for (i = 0; i < spawn.length; i++) {
console.log(spawn[i])
//alert("i one")
var newdom = dom + "_" + i;
this.synthesize_elements(spawn[i], dom, newdom)
if (spawn[i].hasOwnProperty('elements')) {
//alert("FOUND")
var newarray = spawn[i]['elements'];
if (newarray.length > 0) {
this.run_loop(newarray, newdom)
}
}
}
}
This is a little old but I encountered a similar issue and found my way here, but I figured it out and thought I'd post the solution if anyone else came across this. The issue in my case (and it looks like in your case as well though I can't be sure) was in the declaration of the for loop:
for (i = 0; i < spawn.length; i++)
You're not declaring that i is a new var. so if anything inside the
this.run_loop(newarray, newdom)
function also manipulates a counter variable called i that isn't declared a new var, it will also change the one in your outer scope, and kick you out of the loop if it goes over the length of spawn
just always declare:
for (var i; i< spawn.length; i++)
in your loops or make sure your counters are unique.
I wrote a function to change the class of elements to change their properties. For some reason, only some of the elements have changed. It took me a few hours to find a solution, but it seems odd to me. Perhaps you can explain this to me.
This isn’t working:
function replace(){
var elements = document.getElementsByClassName('classOne');
for (var i = 0; i < elements.length; i++) {
elements[i].className = 'classTwo';
}
}
See the JSFiddle: only every second item is affected; only every second red element changes color to blue.
So I changed the final expression of the for loop to not increment i anymore:
function replace(){
var elements = document.getElementsByClassName('classOne');
for (var i = 0; i < elements.length; i) { // Here’s the difference
elements[i].className = 'classTwo';
}
}
This works well! It seems as though push is called and no increment is needed. Is this normal? It is different from the examples I’ve seen.
What's going on is an odd side effect. When you reassign className for each element of elements, the element gets removed from the array! (Actually, as # user2428118 points out, elements is an array-like object, not an array. See this thread for the difference.) This is because it no longer has the classOne class name. When your loop exits (in the second case), the elements array will be empty.
You could write your loop as:
while (elements.length) {
elements[0].className = 'classTwo'; // removes elements[0] from elements!
}
In your first case, by incrementing i, you are skipping half of the (original) elements that have class classOne.
Excellent question, by the way. Well-researched and clear.
getElementsByClassName returns a NodeList. A NodeList collection is a live collection, which means that the modification of the document affects the collection. more
Or revert the loop, beginning by length-1 and step down to 0
So I have a block of code that looks like this:
stack.forEach(function(element){
//sys.puts(sys.inspect(element, false, null));
console.log('-----element start-----');
console.log(element.type + ':' + element.raw);
console.log('-----element end-----');
if(element.children){
element.children.forEach(function(childElement){
stack.push(childElement);
});
}
});
The issue is that this is not behaving like I would except a stack to behave and wondering if this in an issue with JavaScript itself. The issue I am seeing if that when I call the stack.push for each child to add it to the stack for processing, the initial stack.forEach() does not seem to be picking it up and it is only logging to top level elements. If I try to do another stack.forEach() directly after this one, it then displays the next level of child elements so I know .push is diffidently adding the child elements to the stack.
It almost seems like .forEach() grabs the data as it is and does not get an updates if they happen within the foreach. Is this true for JavaScript? Is there a different way I can accomplish the same thing (be able to process a top level element and all levels of children elements below it)?
Your supposition is correct. The ForEach function takes a snapshot of the array and any updates will not be processed.
You are trying to implement a classic tree traversal algorithm which can most easily be coded as a recursive function as follows :
var stack = [];
function traverse (element) {
//sys.puts (sys.inspect(element, false, null));
console.log ('-----element start-----');
console.log (element.type + ':' + element.raw);
console.log ('-----element end-----');
stack.push (element); // for preorder traversal (parent before children)
if (element.children)
element.children.forEach (traverse);
// stack.push (element); // for postorder traversal (children before parent)
};
traverse (document.body); // for example
Yes, .forEach does only iterate over the range the array initially has, it works with a copy of the .length value. If you want to change that behavior, I'd recommend a classical loop which queries the length property each time:
var stack = […];
for (var i=0; i<stack.length; i++) {
// process stack[i]
stack.push(…);
}
or even a more list-like behavior with
var stack = […];
while (stack.length) {
var cur = stack.shift();
// process cur
stack.push(…); // with variable arguments, you want to stop somewhen :-)
}
If you don't want/need to maintain the stack yourself, a recursive function is the proper choice for tree traversal.
This has been a challenge for me...
I have a set of nodes in an XML doc. I need to sort them based on a certain node value. So if I iterate through the nodes, and then the node value matches my criteria, I want it to go to the end.
Problem is, of course as soon as I swap, as nodes are in a live set, the iteration pointer misses one entry of course, as the appendChild is operating on a live-set.
This is my code so far, but as I said, it may miss an entry due to the swapping:
for (var i=1; i <= nElem; i++)
{
var node = getNode(dom,"//item[" + i + "]");
var state = getNodeValue(dom,"//item[" + i + "]/state");
if ((state != 'XX') && (i != nElem))
{
node.parentNode.appendChild(node);
}
}
What I actually want is that all items in state "XX" are at the top.
Has anyone an intelligent idea to this?
Thanks
You could use array.sort() and pass a custom sort routine:
var nodes = getNode(dom, "//item"); gets you an array of items
next, remove the entries in nodes from the dom
do an nodes.sort(sortfunction) where sortfunction is sortfunction(a,b)
implement sortfunction so that it returns
-1 if a shall be lower than b
0 if equal
1 if a shall be higher than b
add the entries of nodes back to the dom
I think, that would do it (as long as I'm not missing something).