I need some help with JavaScript.I am working with XML so i need on which i am implementing JavaScript, and i would like to do sorting of the elements node name. Please see the following figure which will make you clear what i am talking about. Please i want the code with JavaScript not Jquery.
Live Fiddle
UnSorted: Sorted:
bookstore bookstore
xbook abook
title author
author price
year title
price year
cbook cbook
gbook gbook
abook xbook
function generate(node) {
if (node.nodeType != 1) return "";
var html = "<li>" + node.nodeName;
var htmlForChildNodes = "";
for (var i = 0; i < node.childNodes.length; i++) {
htmlForChildNodes += generate(node.childNodes[i]);
}
if (htmlForChildNodes) {
html += "<ul>" + htmlForChildNodes + "</ul>";
}
html += "</li>";
return html;
}
Thank you
You should parse the xml document and put it into a javascript array of objects. Those are easily sortable.
So instead of calling generate(xmlDoc.documentElement) and have it return html straight from the xml you should add a parse(xmlDoc.documentElement) function that returns an array of arrays. After that change your generate() function to take an object with a children property that is an array instead of the original xml dom.
Parse should look something like this:
function parse(node) {
if (node.nodeType != 1) return null;
var result = {name: node.nodeName};
var children = [];
for (var i = 0; i < node.childNodes.length; i++) {
var child = parse(node.childNodes[i]);
if (child)
{
children.push(child);
}
}
result.children = children;
return result;
}
Now you can sort the arrays with children by creating your own comparator function.
After sorting you call the (changed) generate() function that will iterate over the objects/arrays to produce the html.
Here is a fiddle. I've left the changes of the generate() function to you. (Use your browser console to inspect the generated data structure)
Related
I use the below JavaScript function to generate a html table from a 2D data array with numbers called d.
function matrix(d) {
var html = '<table>';
for (var i = 0; i < d.length; i++) {
html += '<tr>';
for (var j = 0; j < d[i].length; j++) {
html += '<td>' + d[i][j] + '</td>';
}
html += "</tr>";
}
return html;
}
That function works well but I can only look at the pretty html output and not use the data in other functions so now I am trying to write another function that goes in the opposite direction and converts a html table to a 2D array with numbers. So far I got the below function:
function tableToArray(w) {
var myTableArray = [];
$(w).each(function() {
var arrayOfThisRow = [];
var tableData = $(this).find('td');
if (tableData.length > 0) {
tableData.each(function() { arrayOfThisRow.push($(this).text()); });
myTableArray.push(arrayOfThisRow);
}
});
console.log(myTableArray);
return myTableArray;
}
but that function is not working very well. If I call tableToArray(matrix([[1,2,3],[4,5,6]])) the result in the console.log is a 1D column array with strings (or maybe a 1D row array with string hard to tell the difference in the console log) that looks like "1","2","3","4","5","6". I want a 2D array with numbers and not strings since my original array was a 2D array with numbers. What JavaScript function code will give me that 2D array with numbers?
Assigning the data array d to a global variable (without var) or a window.variable is not an option because I need to be able to call the function likes this: tableToArray(matrix([[1,2,3],[4,5,6]])).
I have googled around all day to find a previous written function that does this but I have not managed to find a working one nor have I been successful in writing one myself.
I've got a working recursive function which goes through an XML doc looking for a matching node name, and then logging matching values...I'm trying to modify it to return a string or an array, and can't figure it out.
This is in Google Apps script. I've tried passing in a blank string into the function, and then returning it at the end, but it doesn't work. Here is the working Logger function:
function logChildren(elements, dataRequired){
for (var i = 0; i < elements.length; i++) {
if (elements[i].getName() == dataRequired){
Logger.log(elements[i].getText());
}
if(elements[i].getContentSize() > 1){
var children = elements[i].getChildren();
logChildren(children, dataRequired);
}
}
};
I tried passing in an empty string, and then returning it like this but it doesn't work:
function logChildren(elements, dataRequired, str){
for (var i = 0; i < elements.length; i++) {
if (elements[i].getName() == dataRequired){
str = str + ", " + elements[i].getText();
}
if(elements[i].getContentSize() > 1){
var children = elements[i].getChildren();
logChildren(children, dataRequired, str);
}
}
return str
};
How do I get a string or array OUT of this function, rather than just console logging it?
Instead of returning str try without it, because str will have all the values. If you return str it might collapse the current iteration. Please let us know whether this worked
Providing your elements is already parsed and valid, this should work.
function logChildren(elements, dataRequired){
values = [];
req = elements.getElementsByTagName(dataRequired);
for (var i = 0; i < req.length; i++) {
values.push(req[i].childNodes[0].nodeValue);
}
return values
};
elements = "<house>" +
"<name>hello</name>" +
"<town>world</town>" +
"<name>cat</name>" +
"<folder>" +
"<name>kitty</name>" +
"</folder>" +
"</house>";
p = new DOMParser();
elements = p.parseFromString(elements, "text/xml");
newValues = logChildren(elements, "name")
console.log(newValues);
I've included my own little xml just to test, and it returns an array.
As you can see, getElementsByTagName even returns values in sub folders.
You should use a global variable or another function, so that the output variable str is outside the scope of the recursed function.
var str = "";//holds all data of recursion
function logChildren(elements, dataRequired){
..
str += ", " + elements[i].getText();
..
}
Let's say I have a paragraph of text in a HTML file. Using TreeWalker I pushed every non-empty text node to an array:
function getTextNodes(elem) {
var textNodes = [];
var nextNode;
var treeWalker = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT, {
acceptNode: function(node) {
return node.textContent !== "\n" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
}, false);
var counter = 1;
while (nextNode = treeWalker.nextNode()) {
textNodes.push(nextNode);
console.log("Pushed " + counter + " times.");
counter++;
}
return textNodes;
}
When I try to change the contents of the array, like replacing every element with the string "changed ", nothing happens in the browser window.
Is it somehow possible storing the nodes by reference, such that every time they are changed, the text in the browser changes, too?
EDIT:
function changeTextNodes(elem) {
for (var i = 0; i < elem.length; i++) {
elem[i] = "change ";
}
}
The code that changes the array elements.
EDIT2:
$(document).ready(function() {
changeTextNodes(getTextNodes(document.body));
});
Here's how the 2 functions are called.
Your code that attempts to change the text examines the array result from gathering up the nodes. That array consists of nodes, so if you want to modify the content you need to do that through the node API. Currently, your code just modifies the array contents, which will have no effect on the DOM; it's just a JavaScript array.
To change the content, you'd modify the nodeValue property:
for (var i = 0; i < elem.length; i++) {
elem[i].nodeValue = "change ";
}
After being stuck for a few hours on this problem, i think it is time for call for help on this.
Situation
I have a XML file which i need to filter and group. I've managed to filter it with the :Contains part. I've also determined the nodes on which i need to group (the getGroups function gives those back to me). Now i want to create a new XML with the filtered values and grouped by the returned keys.
Code
var XMLElement = document.createElement("DataElementsCalc");
jQuery(xml).find("DataElements " + topNodes + filter).each( function() {
var dataSetTemp = this.parentNode;
if(calculation1 != "")
{
var groupKeys = getGroups(dataSetTemp,calculation1);
var tempXML = XMLElement;
jQuery(groupKeys).each(function (key,value) {
var tempValue = 'a' + value.toLowerCase().replace(/\W/g, '');
if(tempXML.getElementsByTagName(tempValue).length > 0)
{
tempXML = tempXML.getElementsByTagName(tempValue);
}
else
{
var Node = document.createElement(tempValue);
tempXML.appendChild(Node);
tempXML = Node;
}
});
var Node = document.createElement("InfoSet");
var x = dataSetTemp.childNodes;
for (i=0; i < x.length; i++)
{
if(x[i].nodeType == 1)
{
var tempElement = document.createElement(x[i].nodeName);
tempElement.innerHTML = x[i].childNodes[0].nodeValue;
Node.appendChild(tempElement);
}
}
tempXML.appendChild(Node);
}
});
Explanation
As said in the situation, i already filtered the XML and have the groupNames from the getGroups function. There are a few other things i need to explain for this code:
tempValue is being build as a a + value.toLowerCase().replace(/\W/g, '');. This is being done because i possible get dates into the groupKeys. This way the node name is getting a working name (i received errors on other ways).
I want to create a new XML which is leveled by the groups. If a group already exists, i want to create a new element in it, not get a new group with the same name. (my problem at the moment).
Problem
As mentioned above, the groups aren't checked properly. Firstly: tempXML.getElementsByTagName(tempValue).length returns the error that the function tempXML.getElementsByTagName does not exists. Secondly: If i change this to document.getElemetsByTagName I get a lot of the same nodes in my XML file.
Effect
The grouping effect doesn't work as it should. I get OR an error, OR a lot of the same nodes in my DataElementsCalc.
Questions
How can i solve this? How do create nodes beneath specific nodes (for if there is a group A beneath group 1 as well as beneath group 2)?
Tried
Change tempXML to document on different places (all getElementsByTagName, at the same time or not). Looked for another way to create a XML which is easier to handle (haven't found one, yet)
As mentioned by myself in the comments of the question:
I also don't see anything in the source code for this (maybe this is the reason why it doesn't work??)
I tried to place the XMLElement into an existing element on my webpage (like this:
var XMLElement = document.createElement("DataElementsCalc");
jQuery('.basicData').append(XMLElement);
in which basicData is a class of an existing element).
Now i do get a list of all elements ordered on the groups i wanted.
Final version
var XMLElement = jQuery("<DataElementsCalc/>");
jQuery('.basicData').append(XMLElement);
jQuery(xml).find("DataElements " + topNodes + filter).each( function()
{
aantalElementsTotal++;
var dataSetTemp = this.parentNode;
if(calculation1 != "")
{
var groupKeys = getGroups(dataSetTemp,calculation1);
var tempXML = XMLElement;
var groupId = '';
jQuery(groupKeys).each(function (key,value) {
var tempValue = 'a' + value.toLowerCase().replace(/\W/g, '');
groupId += 'a' + value.toLowerCase().replace(/\W/g, '');
if(jQuery("#" + groupId).length > 0)
{
tempXML = jQuery("#" + groupId);
}
else
{
var Node = jQuery("<"+tempValue+"/>");
jQuery(Node).attr('id', groupId);
jQuery(tempXML).append(Node);
tempXML = Node;
}
});
var Node = jQuery("<InfoSet/>");
var x = dataSetTemp.childNodes;
for (i=0; i < x.length; i++)
{
if(x[i].nodeType == 1)
{
var tempElement = jQuery("<"+x[i].nodeName+"/>");
jQuery(tempElement).text(x[i].childNodes[0].nodeValue);
jQuery(Node).append(tempElement);
}
}
jQuery(tempXML).append(Node);
}
});
I am using the following code to get the answers and questions of json file:
$(document).ready(function () {
$.getJSON("data.json", function (json) {
var anserFor1st = json.questions[0].answers;
var anserFor2nd = json.questions[1].answers; //If it's more than two use a loop
document.getElementById("content").innerHTML = JSON.stringify(anserFor1st) + "<br/>" + JSON.stringify(anserFor2nd);
var aString = "";
Object.keys(anserFor1st).forEach(function (k) {
aString += anserFor1st[k] + "<br/>";
});
Object.keys(anserFor2nd).forEach(function (k) {
aString += anserFor2nd[k] + "<br/>";
});
document.getElementById("content").innerHTML = aString;
});
});
Instead I want to change dynamically the index of the answers&question, aka write the following:
var anser = json.questions[i].answers;
I have to change the index according to event-listener to clicking next and forward buttons in the html file.
How do I change the index dynamically?
I think you mean you want this:
$(document).ready(function () {
$.getJSON("data.json", function (json) {
var answers = [];
var aString = "";
for (var i = 0; i < json.questions.length; i++)
{
answers[i] = json.questions[i].answers;
document.getElementById("content").innerHTML += JSON.stringify(answers[i]) + "<br/>";
for (var j = 0; j < answers[i].length; j++)
{
aString += answers[i][j] + "<br/>";
}
}
document.getElementById("content").innerHTML = aString;
});
});
This code does exactly what yours did, but there are some things in there that make no sense (such as first loading the json.questions[i].answers into the content element, only to then replace it a few lines later with the contents of aString.) You also stringify the JSON before placing it into the DOM, but I'd have expected you to do the opposite (parsing it, since you already just retrieved it as JSON, so stringifying it again shouldn't do anything).
Basically, I'm not entirely sure what you mean by your question, so if I misunderstood please clarify your question.
EDIT: if your problem is just remembering which question you're on, make a variable to hold the number of the current question (e.g. var currentQuestion = 1;), and then just change that based on what button is clicked.
For example:
$("#buttonPrevious").on("click", function (e)
{
currentQuestion--;
});
$("#buttonNext").on("click", function (e)
{
currentQuestion++;
});
Of course you can check for minimum/maximum question numbers (for example, your max number of questions can be equal to the last value of i in the loop above).