populate select element based on json - javascript

How could I populate a second select element? I've figured out how to do the first one. But how could I do the same for the second depending on which "Make" is selected? I've tried to talk myself through it while taking small steps but I'm thinking this may be too advanced for me.
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"},{"name":"Spider","value":"22172"}]}';
var carobj = eval ("(" + cars + ")");
var select = document.getElementsByTagName('select')[0];
//print array elements out
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
select.options.add(new Option(d.name, i))
};

If I read your question right, you want to populate a second select with the models for the make in the first select. See below for a purely JS approach (with jsfiddle). If possible, I would recommend looking into jQuery, since I would prefer a jQuery solution.
http://jsfiddle.net/m5U8r/1/
var carobj;
window.onload = function () {
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"}, {"name":"Spider","value":"22172"}]}]}';
carobj = eval ("(" + cars + ")");
var makes = document.getElementById('make');
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
makes.options.add(new Option(d.name, i));
}
makes.onchange = getModels;
getModels();
}
// add models based on make
function getModels () {
var makes = document.getElementById('make');
var make = makes.options[makes.selectedIndex].text;
for (var i = 0; i < carobj.USED.length; i++) {
if (carobj.USED[i].name == make) {
var models = document.getElementById('model');
models.options.length = 0;
for (var j= 0; j < carobj.USED[i].models.length; j++) {
var model = carobj.USED[i].models[j];
models.options.add(new Option(model.name, j));
}
break;
}
}
}
I would also recommend looking into safer JSON parsing. There is a security risk in using eval if it runs on any user input. You could look into JSON.org and their json2.js. Or if you want to use jQuery: parseJSON. Below is the jQuery version:
jQuery.parseJSON(jsonString);
JSON parsing tips from: Safely turning a JSON string into an object.

Related

Strange error when appending elements in JavaScript

I've been getting a problem when trying to append an element in JavaScript, the error I've been getting looks a bit like this:
Uncaught TypeError: Failed to execute appendChild on Node: parameter 1 is not of type Node.
I'm also using using a framework called Interact.js just so you know
here's the peice of code that the browser isn't happy about:
var tempText = [];
var classNum = event.relatedTarget.getElementsByClassName('text');
var newCont = document.createElement('div');
for(var i = 0; i < classNum.length; i++){
tempText.push(event.relatedTarget.getElementsByClassName('text')[i].textContent);
}
for(var i = 0; i < tempText.length; i++){
var pText = document.createElement('p').appendChild(tempText);
newCont.appendChild(pText[i]);
}
var placement = document.getElementById('toolbar')[0];
placement.appendChild(newCont);
I just noticed a small mistake. The document.getElementById returns only a single object. So don't use the [0]:
var placement = document.getElementById('toolbar'); // There is no [0]. Remove it.
placement.appendChild(newCont);
But the whole thing is really easy to do using jQuery. Since you are fine with using a jQuery solution, read on. Please include the jQuery library by adding this piece:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
And the JavaScript would be:
var tempText = [];
var classNum = event.relatedTarget.getElementsByClassName('text');
var newCont = document.createElement('div');
for (var i = 0; i < classNum.length; i++) {
tempText.push(event.relatedTarget.getElementsByClassName('text')[i].textContent);
}
for (var i = 0; i < tempText.length; i++) {
// Change here:
var pText = $("<p />", {html: tempText});
$(newCont).append(pText);
}
var placement = $('#toolbar');
placement.append(newCont);
Since I am unaware of the HTML underlying, I just guessed it and converted a few to jQuery.
As you tagged your question with jquery, you can condense your code to this:
var $newCont = $('<div>');
$('.text', event.relatedTarget).each(function() {
$newCont.append($('<p>').append($(this).text()));
})
$('#toolbar').append($newCont);
Or in a functional programming way:
$('#toolbar').append($('<div>').append(
$('.text', event.relatedTarget).map(function() {
return $('<p>').append($(this).text());
}).get()
));

create names for automatically created vars

In JavaScript I have a for snippet to create new input elements
for(var g = 0; g < psi.length; g++) {
var newtextLink+(g+1)= document.createElement('input');
//continue with setting attributes
}
I want to put together the word newtextLink and the var g to have something like newtextLink2 everytime for is executed...How can I achieve that?
This is where you usually want an array instead:
var newtextLinks = [];
for(var g = 0; g < psi.length; g++)
{
newtextLinks[g] = document.createElement('input');
}
Then use them via index variables like that (newtextLink[g], newtextLink[0], etc.).
Alternately, there can be places (probably not here) where you really do want names. You can do that with an object:
var newtextLinks = {};
for(var g = 0; g < psi.length; g++)
{
newtextLinks["name" + (g+1)] = document.createElement('input');
}
Now you have newtextLinks.name1, newtextLinks.name2, and so on.
But for this purpose, an array seems best.
If you insist on using the variables, you can do it using the window object:
window['newtextLink' + (g+1)] = document.createElement('input');
Otherwise use an array:
var newTextLinks = [];
...
newTextLinks[g] = document.createElement('input');
Try
this['newtextLink' + (g + 1)] = ...;
function createVariables(){
var newtextLink= [];
for (var i = 0; i <= psi.length; ++i) {
newtextLink[i] = document.createElement('input');
}
return newtextLink;
}
you have newtextLink[0] ... newtextLink[n]
you have similar question here:
JavaScript: Dynamically Creating Variables for Loops

Javascript - efficiently insert multiple HTML elements

I'd like to create a select element with a list of a user's Facebook friends (obtained as a JSON object). I hardcode <select id="friends"></select> into my HTML, then use the following Javascript code to parse the JSON and insert each friend as an option of the select element:
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
msgContainer.appendChild(document.createTextNode('<option value="'+response.data[i].id+'">'+response.data[i].name+'</option>'));
}
document.getElementById("friends").appendChild(msgContainer);
This almost works, except that it inserts < and > instead of < and >. How can I fix it, and is there a more efficient way to insert multiple HTML elements using pure Javascript (not JQuery)?
Not sure why you're creating a text node, but it would seem that you want to create option elements, so you could use the Option constructor instead.
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
msgContainer.appendChild(new Option(response.data[i].name, response.data[i].id));
}
document.getElementById("friends").appendChild(msgContainer);
Or you can use the generic document.createElement().
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
var option = msgContainer.appendChild(document.createElement("option"));
option.text = response.data[i].name;
option.value = response.data[i].id;
}
document.getElementById("friends").appendChild(msgContainer);
It's nice to have a helper function for creating elements and setting properties at the same time.
Here's a simple example of one:
function create(name, props) {
var el = document.createElement(name);
for (var p in props)
el[p] = props[p];
return el;
}
It can be expanded to cover some specific needs, but this will work for most cases.
You'd use it like this:
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
msgContainer.appendChild(create("option", {
text: response.data[i].name,
value: response.data[i].id
}));
}
document.getElementById("friends").appendChild(msgContainer);
Try this in your for loop instead:
var o = document.createElement('option');
o.setAttribute('value', response.data[i].id);
o.appendChild(document.createTextNode(response.data[i].name));
msgContainer.appendChild(o);
For those who need similar functionality, you can generate an html snippet using template literals and insert it using innerHTML property. Plus you can set attributes and selected while iterating over the items:
const el = document.createElement('select');
el.innerHTML = ['John', 'Sally', 'Betty'].reduce((acc, prev, i) => {
if (i === 1) {
return acc + `<option selected>${prev}</option>`;
}
return acc + `<option>${prev}</option>`;
}, '');
const root = document.querySelector('#app');
root.appendChild(el);
In modern browsers this is faster than creating elements one by one imperatively.

json sibling data

(forgive me if I use slightly incorrect language - feel free to constructively correct as needed)
There are a couple posts about getting data from JSON data of siblings in the returned object, but I'm having trouble applying that information to my situation:
I have a bunch of objects that are getting returned as JSON from a REST call and for each object with a node of a certain key:value I need to extract the numeric value of a sibling node of a specific key. For example:
For the following list of objects, I need to add up the numbers in "file_size" for each object with matching "desc" and return that to matching input values on the page.
{"ResultSet":{
Result":[
{
"file_size":"722694",
"desc":"description1",
"format":"GIF"
},
{
"file_size":"19754932",
"desc":"description1",
"format":"JPEG"
},
{
"file_size":"778174",
"desc":"description2",
"format":"GIF"
},
{
"file_size":"244569996",
"desc":"description1",
"format":"PNG"
},
{
"file_size":"466918",
"desc":"description2",
"format":"TIFF"
}
]
}}
You can use the following function:
function findSum(description, array) {
var i = 0;
var sum = 0;
for(i = 0; i < array.length; i++) {
if(array[i]["desc"] == description && array[i].hasOwnProperty("file_size")) {
sum += parseInt(array[i]["file_size"], 10);
}
}
alert(sum);
}
And call it like this:
findSum("description1", ResultSet.Result);
To display an alert with the summation of all "description1" file sizes.
A working JSFiddle is here: http://jsfiddle.net/Q9n2U/.
In response to your updates and comments, here is some new code that creates some divs with the summations for all descriptions. I took out the hasOwnProperty code because you changed your data set, but note that if you have objects in the data array without the file_size property, you must use hasOwnProperty to check for it. You should be able to adjust this for your jQuery .each fairly easily.
var data = {};
var array = ResultSet.Result;
var i = 0;
var currentDesc, currentSize;
var sizeDiv;
var sumItem;
//Sum the sizes for each description
for(i = 0; i < array.length; i++) {
currentDesc = array[i]["desc"];
currentSize = parseInt(array[i]["file_size"], 10);
data[currentDesc] =
typeof data[currentDesc] === "undefined"
? currentSize
: data[currentDesc] + currentSize;
}
//Print the summations to divs on the page
for(sumItem in data) {
if(data.hasOwnProperty(sumItem)) {
sizeDiv = document.createElement("div");
sizeDiv.innerHTML = sumItem + ": " + data[sumItem].toString();
document.body.appendChild(sizeDiv);
}
}
A working JSFiddle is here: http://jsfiddle.net/DxCLu/.
That's an array embedded in an object, so
data.ResultSet.Result[2].file_size
would give you 778174
var sum = {}, result = ResultSet.Result
// Initialize Sum Storage
for(var i = 0; i < result.length; i++) {
sum[result[i].desc] = 0;
}
// Sum the matching file size
for(var i = 0; i < result.length; i++) {
sum[result[i].desc] += parseInt(result[i]["file_size"]
}
After executing above code, you will have a JSON named sum like this
sum = {
"description1": 20477629,
"description2": 1246092
};
An iterate like below should do the job,
var result = data.ResultSet.Result;
var stat = {};
for (var i = 0; i < result.length; i++) {
if (stat.hasOwnProperty(result[i].cat_desc)) {
if (result[i].hasOwnProperty('file_size')) {
stat[result[i].cat_desc] += parseInt(result[i].file_size, 10);
}
} else {
stat[result[i].cat_desc] = parseInt(result[i].file_size, 10);
}
}
DEMO: http://jsfiddle.net/HtrLu/1/

Finding an object and returning it based on search criteria

I have been searching online all day and I cant seem to find my answer. (and I know that there must be a way to do this in javascript).
Basically, I want to be able to search through an array of objects and return the object that has the information I need.
Example:
Each time someone connects to a server:
var new_client = new client_connection_info(client_connect.id, client_connect.remoteAddress, 1);
function client_connection_info ( socket_id, ip_address, client_status) {
this.socket_id=socket_id;
this.ip_address=ip_address;
this.client_status=client_status; // 0 = offline 1 = online
};
Now, I want to be able to search for "client_connection.id" or "ip_address", and bring up that object and be able to use it. Example:
var results = SomeFunction(ip_address, object_to_search);
print_to_screen(results.socket_id);
I am new to javascript, and this would help me dearly!
Sounds like you simply want a selector method, assuming I understood your problem correctly:
function where(array, predicate)
{
var matches = [];
for(var j = 0; j < array.length; j++)
if(predicate(j))
matches.push(j);
return matches;
}
Then you could simply call it like so:
var sample = [];
for(var j = 0; j < 10; j++)
sample.push(j);
var evenNumbers = where(sample, function(elem)
{
return elem % 2 == 0;
});
If you wanted to find a specific item:
var specificguy = 6;
var sixNumber = where(sample, function(elem)
{
return elem == specificguy;
});
What have you tried? Have you looked into converting the data from JSON and looking it up as you would in a dictionary? (in case you don't know, that would look like object['ip_address'])
jQuery has a function for this jQuery.parseJSON(object).
You're going to need to loop through your array, and stop when you find the object you want.
var arr = [new_client, new_client2, new_client3]; // array of objects
var found; // variable to store the found object
var search = '127.0.0.1'; // what we are looking for
for(var i = 0, len = arr.length; i < len; i++){ // loop through array
var x = arr[i]; // get current object
if(x.ip_address === search){ // does this object contain what we want?
found = x; // store the object
break; // stop looping, we've found it
}
}

Categories