Javascript for Mozilla:
while (result)
{
alert(result.childNodes[0].nodeValue);
//txt=txt+result.childNodes[0].nodeValue+"<br/>";
result=nodes.iterateNext();
}
This is intended to return a series of strings from an xml file. When I use the alert line, it alerts as expected with the proper strings in a series. The "txt" variable on the commented line is supposed to go to an innerHTML further down in the function. But Firebug keeps telling me that the result.childNodes[0] is undefined. Why would it be defined in the alert but not the next line?
I hope that's enough code to determine the problem...if not I will post more.
Thanks for help
[edit]
this is the definition of result:
var x=xmlDoc.responseXML;
var nodes=x.evaluate(path, x, null, XPathResult.ANY_TYPE, null);
var result=nodes.iterateNext();
I am retrieving XML
[edit]
okay I put the iterator in a for loop like this:
for (var i=0; i<2; i++)
{
var res=(xmlNodes.childNodes[0].nodeValue);
txt=txt+res+"<br/>";
xmlNodes=xmlQuery.iterateNext();
}
because I had a theory that once the iteration was null the loop would fail. So that's what happened--it worked until I set the loop one instance higher than the amount of nodes available. now I am trying to figure out how to get the "length" of the node-set. How do I do that?
I don't see any problems in the code you have shown so far. If childNodes[0] is undefined, then it has to be a text node or an empty node, and you should see an exception when trying to access a property such as nodeValue of childNodes[0] which is undefined. The exception will show up on alert or concatenation, or any other type of access.
This is the answer to your updated question.
now I am trying to figure out how to get the "length" of the node-set. How do I do that?
You can have the following types of node-sets returned from the evaluate function:
Iterators
Snapshots
First Nodes
I'll skip "first nodes" as that doesn't apply in this situation.
Iterators
With iterators, you only get an iterateNext() method for traversing nodes sequentially. Iterators refer to live nodes, meaning if the nodes in the document were to change while you are traversing the resultset, the iterator will become invalid.
Here's an example with using an iterator to go over each resulting node:
var results = doc.evaluate("expr", doc, null, ORDERED_SNAPSHOT, null);
var node;
while(node = results.iterateNext()) {
console.log(node);
}
If you want to use iterators, and find the number of matching results, a wrapper function that iterates through all nodes and returns them in an array might be useful.
function evaluateXPath(document, expression) {
var ANY_TYPE = XPathResult.ANY_TYPE;
var nodes = document.evaluate(expression, document, null, ANY_TYPE, null);
var results = [], node;
while(node = nodes.iterateNext()) {
results.push(node);
}
return results;
}
Get nodes as an array and loop the array:
var results = evaluateXPath(doc, "expr");
for(var i = 0; i < results.length; i++) {
console.log(results[i]);
}
Snapshots
Snapshots provide a static result of the nodes at the time of querying. Changes to the document will not affect this snapshot. Useful interfaces here will be the snapshotLength property, and snapshotItem(index) function.
Here's an example using a snapshot result:
var ORDERED_SNAPSHOT = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
var results = doc.evaluate("expr", doc, null, ORDERED_SNAPSHOT, null);
for(var i = 0; i < results.snapshotLength; i++) {
var node = results.snapshotItem(i);
console.log(node);
}
See a working example.
It seems you are developing this for Firefox. Did you consider using E4X for this purpose? It provides a really easy interface for dealing with XML documents - for creating, manipulating, and querying.
now I am trying to figure out how to
get the "length" of the node-set. How
do I do that?
In XPath this is achieved using the standard XPath count() function.
count(someExpression)
evaluates to the number of nodes selected by someExpression.
Related
I am just reading through a book but it didn't mention using for this way. The most common way I can understand but this I have no idea how it start and end.
this is a function which has a for loop in it and what's being inserted into the argument is an object which inherits objects and objects like a list.
function listToArray(list) {
var array = [];
for (var node = list; node; node = node.rest)
array.push(node.value);
return array;
}
var object = {
value: 10,
rest: {
value: 20,
rest: {
value: 30,
rest: null
}
}
}
console.log(listToArray(object));
// → [10, 20, 30]
for (var node = list; node; node = node.rest) this is the part where I don't understand how it works. How it start or end.
I just have a simple guess myself to start off, node is an object so if node != null then node = list and node = node.rest which is the next object
A for loop take three arguments:
for ([initialization]; [condition]; [final-expression])
var node = list; runs when the loop is started.
node is the condition, if it is false, the loop won't go around again.
node = node.rest is run at the end of the loop.
So:
It sets node to list and then keeps replacing node with the value of the previous nodes rest property.
Eventually node is { value: 30, rest: null }, so it sets node to null then tests if null is true and then stops because it isn't.
Like in any other c based language, for loop is basically formed this way: for(initialization;condition;incrementation)
In this code, the initialization part is defining a new var named node, and initializing it´s value to the same as the argument list.
The condition part node in javascript means, while node != null (or unassigned)
The incrementation part is setting node to the value of node.rest.
I dunno what node.rest does, but I suppose it will move to the next node in the tree/graph.
So in this case, the for starts when list is assigned, and ends when node.rest returns null/unassigned.
This is actually, really interesting! But what you have here is a for loop used in a pretty creative way to iterate deeper into an object.
for (var node = list; node; node = node.rest)
If we think about for loops in a traditional sense, something like
for (var i = 0; i < 10; i++)
We can draw a few parallels.
If you look at the MDN for a for loop, you see that it's broken up into three sections:
for ([initialization]; [condition]; [final-expression])
Initialization -- Specify variables, etc that will be used at the start of the loop, in this case our starting node is equal to the list passed in.
Condition -- Now here's something a little different, but basically this middle statement is condition to determine if the loop should execute. Most of the time you see i < 10 or some numeric comparison because that's a common use case. This example is leveraging the truthiness of objects in javascript. That is, an object, taken by itself evaluates to true.
ex. if({}) // will be true The fact that node is placed there by itself is basically saying "Look at the truthiness of node, if the object exists, then let's run the loop! Otherwise node will be undefined, and undefined is a falsey value in javascript.
Lastly we have the final expression, or what I like to call the 'update' section. We're essentially assigning node to be equal to its child property, which means we're iteratively going deeper and deeper into these objects. if node.rest exists, then the next iteration of the loop will start there and run itself again if node.rest actually existed in the previous loop.
Good question. I enjoyed that one.
Try this :
Boolean(object.rest); //true
Boolean(object.rest.rest); //true
Boolean(object.rest.rest.rest); //false
First of all, I'm aware there are many questions about closures in JavaScript, especially when it comes to loops. I've read through many of them, but I just can't seem to figure out how to fix my own particular problem. My main experience lies with C#, C++ and some ASM and it is taking some getting used to JavaScript.
I'm trying to populate a 3-dimensional array with new instances of a class (called Tile) in some for loops. All I want to do is pass along a reference to some other class (called Group) that gets instantiated in the first loop (and also added to another array). As you might have guessed, after the loops are done, every instance of the Tile class has a reference to the same Group object, namely the last one to be created.
Apparently instead of passing a reference to the Group object, a reference to some variable local to the function is passed along, which is updated in every iteration of the loop. My assumption is that solving this problem has something to do with closures as this appears to be the case with many similar problems I've come across while looking for a solution.
I've posted some trimmed down code that exposes the core of the problem on jsFiddle:
//GW2 namespace
(function( GW2, $, undefined ) {
//GW2Tile class
GW2.Tile = function(globalSettings, kineticGroup)
{
//Private vars
var tilegroup = kineticGroup;
// console.log(tilegroup.grrr); //Shows the correct value
var settings = globalSettings;
this.Test = function(){
console.log(tilegroup.grrr);
}
this.Test2 = function(group){
console.log(group.grrr);
}
} //Class
}( window.GW2 = window.GW2 || {}, jQuery ));
var zoomGroups = [];
var tiles = [];
var settings = {};
InitArrays();
tiles[0,0,0].Test(); //What I want to work, should give 0
tiles[0,0,0].Test2(zoomGroups[0]); //How I'd work around the issue
function InitArrays(){
var i, j, k, zoomMultiplier, tile;
for(i = 0; i <= 2; i++){
zoomGroups[i] = {};
zoomGroups[i].grrr = i;
tiles[i] = [];
zoomMultiplier = Math.pow(2, i);
for(j = 0; j < zoomMultiplier; j++){
tiles[i,j] = [];
for(k = 0; k < zoomMultiplier; k++){
tile = new GW2.Tile(settings, zoomGroups[i]);
tiles[i,j,k] = tile;
}
}
}
}
Up till now when working with JavaScript, I've generally fiddled with the code a bit to make it work, but I'm tired of using work-arounds that look messy as I know there should actually be some fairly simple solution. I'm just not fond of asking for help, but this is really doing my head in. Any help is very much appreciated.
Multidimensional arrays
The problem
The first issue with your code above is how you are attempting to create multidimensional arrays.
The syntax you are using is:
tiles[0,0,0]
However, the way JavaScript will interpret this is:
tiles[0]
Accessing a multidim array
If you wish to access a multidim array you have to use:
tiles[0][0][0]
And to create a multidim array you would need to do the following:
tiles = [];
tiles[0] = [];
tiles[0][0] = [];
tiles[0][0][0] = 'value';
or:
tiles = [[['value']]];
With respect to your code
In your code you should be using:
tiles[i][j][k] = tile;
But you should also make sure that each sub array actually exists before setting it's value, otherwise you'll get undefined or illegal offset errors.
You can do this by way of:
(typeof tiles[i] === 'undefined') && (tiles[i] = []);
(typeof tiles[i][j] === 'undefined') && (tiles[i][j] = []);
tiles[i][j][k] = tile;
Obviously the above can be optimised depending on how you are traversing your loops i.e. it would be best to make sure the tiles[i] level exists as an array before stepping in to the the [j] loop, and then not worry about checking it's existence again whilst stepping j.
Other options
Depending on what your dataset is, or at least what you hope to do with the tiles array it can be worth considering using an object instead:
/// set up
tiles = {};
/// assignment
tiles[i+','+j+','+k] = 'value';
However this method is likely to be slower, although I've been proved wrong a number of times by my assumptions and differing JavaScript interpreters. This would probably be were jsPerf would be your friend.
Optimisation
One benefit of using the tiles[i][j][k] approach is that it gives you the chance to optimise your references. For example, if you were about to process a number of actions at one level of your multidimensional array, you should do this:
/// set up
var ij = tiles[i][j];
/// use in loops or elsewhere
ij[k] = 'value'
This is only of benefit if you were to access the same level more than once however.
I am trying to list out all the elements inside an array and as you can see Company has three levels, but I've only written the script to print the output until two levels. How do I access the third level? What should be the array that I should be using inside the third for loop?
What you're looking for is recursion.
Here is a fixed version of your fiddle: http://jsfiddle.net/jEmf9/
function generateEntity(obj) {
var html = [];
var name = obj.entity;
html.push('<li>');
html.push(name);
html.push('</li>');
var arrayName = name.replace(/\s/gi, '_');
if (obj[arrayName] == undefined) {
return html.join('');
}
var entity = obj[arrayName];
for (var i = 0; i < entity.length; i++) {
html.push('<ul>');
html.push(generateEntity(entity[i]));
html.push('</ul>');
}
return html.join('');
}
In your case you do not need a special technique for accessing the third level. You need to write a recursive tree walking function so that you can render a tree of any depth.
I've done a quick patch of your code here: http://jsfiddle.net/rtoal/xcEa9/6/
Once you get things working as you like, you can work on forming your html. Your repeated string concatenation using += is known to be extremely inefficient, but that is outside the scope of this question. :)
var brands = document.getElementsByName("brand");
for(var brand in brands){
$("input[name='brand']").eq(brand).click(function(){
alert("hello22");
loadDataFN(1);
});
}
This code is not executing in ie6,
Any help would be appreciated.
The problem is likely that you are trying to use a for-in construct to iterate over a numeric array. This often won't give expected results. Use an incremental for loop instead:
var brands = document.getElementsByName("brand");
// Use an incremental for loop to iterate an array
for(var i=0; i<brands.length; i++){
$("input[name='brand']").eq(brands[i]).click(function(){
alert("hello22");
loadDataFN(1);
});
}
However,
after seeing the first part of your code, the loop appears unnecessary. You should only need the following, since you are assigning the same function to all brand inputs.
// These will return the same list of elements (as long as you don't have non-input elements named brand)
// though the jQuery version will return them as jQuery objects
// rather than plain DOM nodes
var brands = document.getElementsByName("brand");
$("input[name='brand']");
Therefore, the getElementsByName() and loop are not necessary.
$("input[name='brand']").click(function() {
alert("hello22");
loadDataFN(1);
});
for-in loops are used for iterating over the properties of an object, not over the elements of an array.
Why don't you write the code without jQuery if this doesn't work?
Something like this:
function getInputByName(name) {
var i, j = document.getElementsByTagName('input').length;
for(i=0;i<j;++i) { // You can also use getAttribute, but maybe it won't work in IE6
if(document.getElementsByTagName('input')[i].name === name) {
return document.getElementsByTagName('input')[i];
}
}
return null;
}
I don't know jQuery, but maybe you can do something like this:
$(getInputByName('brand')).eq(brand).click(function(){
alert("hello22");
loadDataFN(1);
});
I have a collection of DOM nodes I would like to replace with new DOM nodes.
Due to the way I pass the information into the function I only have a variable named collection which is an object full of HtmlTableRowElements and I would like to replace it with a new variable called Rows.
Here is what I have
var parent = collection[0].parentNode;
parent.innerHTML = "";
parent.appendChild(rows);
however this gives me a long strange error.
uncaught exception: [Exception... "Could not convert JavaScript argument arg 0 [nsIDOMHTMLTableSectionElement.appendChild]" nsresult: "0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)" location: "JS frame :: http://127.0.0.1/test.php :: :: line 103" data: no]
So I have also tried
collection[0].parentNode.innerHTML = "";
collection[0].parentNode.appendChild(rows);
which returns
collection[0].parentNode is null
I do understand why the second option is returning an error. I would imagine the first option is returning an error because I have removed the element the variable is referencing.
I'm beginning to think that looking for the parent and replacing it's contents is not the way to go about this.
any help?
What is in rows? I can't tell from your question
If it’s an array, put the contents into a document fragment and append that:
var fragment = document.createDocumentFragment();
for (var i = 0, l = rows.length; i < l, i++) {
fragment.appendChild(rows[i]);
}
parent.appendChild(fragment);
Putting a bunch of elements in a document fragment is faster than appending them to something in the document one by one, and is a good way to carry around a collection of elements (instead of an array).
If it’s a string, use innerHTML:
parent.innerHTML = rows;
When you call collection[0].parentNode.innerHTML = "", collection gets removed from the document. It still exists only because you’re holding onto it in a JavaScript variable, but no longer has a parent node. You should still be able to grab parent in advance (like you do in the first example), and append things to it:
var parent = collection[0].parentNode;
parent.innerHTML = "";
parent.appendChild(rows);
parent.innerHTML = ""; is one way to clear the content. You then have to iterate over the other elements and append them:
for(var i = 0, l = rows.length; i < l; i++) {
parent.appendChild(row[i]);
}
But afaik it might be problematic to append new rows this way. It might be better to use table.insertRow [MDN].