I don't know javascript much at all, I just like making lists for myself. I am currently trying to create a html page that I can keep track of characters from my favorite game, but I have run across a couple of problems I don't know how to solve.
<form name="inputNightlife" id="inputNightlife">
<h2>Nightlife</h2>
<label for="traits"><b>Traits:</b></label><br>
<select multiple="true" name="traits" id="traits">
<option value="Cologne">Cologne</option>
<option value="Stink">Stink</option>
<option value="Fatness">Fatness</option>
<option value="Fitness">Fitness</option>
</select>
<label for="turnOns"><b>Turn Ons:</b></label><br>
<select multiple="true" name="turnOns" id="turnOns">
<option value="Blonde Hair">Blonde Hair</option>
<option value="Red Hair">Red Hair</option>
<option value="Brown Hair">Brown Hair</option>
<option value="Black Hair">Black Hair</option>
</select>
<p>Select all that apply.</p>
<nav id="box8" class="hide"><table id="menu3"><tr><td rowspan="2" id="soft">
<textarea name="source8" onclick="this.focus();this.select()" cols="40" rows="3" id="result">
</textarea></td><td>
<input type="button" value="Get Code!" onclick="javascript:generateNightlife();"></td>
<td rowspan="2" id="softA">
<img src="./forSCV/icons/nightlife.png" alt="Nightlife" title="Nightlife" id="arrow" onclick="toggle('box8');">
</td></tr><tr><td>
<input type="button" value="Test Code" onclick="javascript:displayNightlife(this.form);">
</td></tr></table></nav></form>
When I click the button, the document.results.endresults.value appears in the text area. I can then copy the results, and save them as html. This is intended to be a page generator (the best I can come up with).
I am not sure how to make traits and turnOns automatically create an array (with spaces) of the chosen options that will then print in the document.result.endresult.value. I did find several different ways to create an array from the forms, but not how to then get it to go into the document.result.endresult.value.
One way Google. And another way Google
Adding...
Ok, I reworked my html to include names and id's, and I found a little better page generator, so I was trying to get that to work. Now I have tried this.
function byId(idStr){return document.getElementById(idStr);}
function getFormValues() {
var traitsSelectElem = byId('traits');
var turnOnsSelectElem = byId('turnOns');
var chosenTraits = getSelectedOptions(traitsSelectElem);
var chosenTurnOns = getSelectedOptions(turnOnsSelectElem);
var i, n, outputStr;
n = chosenTraits.length;
outputStr = '';
for (i = 0; i < n; i ++)
{
if (outputStr != ".")
outputStr += ", ";
outputStr += chosenTraits[i].value;
}
byId('traitsOutput').innerText = outputStr;
n = chosenTurnOns.length;
outputStr = '';
for (i = 0; i < n; i++)
{
if (outputStr != '.')
outputStr += ', ';
outputStr += chosenTurnOns[i].value;
}
byId('turnOnsOutput').innerText = outputStr;
}
function getSelectedOptions(selectElem) {
var i, nOptions = selectElem.options.length;
var result = [];
for (i = 0; i < nOptions; i++)
{
if (selectElem.options[i].selected)
{
result.push(
{
value: selectElem.option[i].value
}
);
}
}
return result;
}
function generateNightlife() {
//nightlife
var traits = getFormValues();
var turnOns = getFormValues();
turnOff = document.inputNightlife.turnOff.value;
perfumeDuration = document.inputNightlife.perfumeDuration.value;
lovePotion = document.inputNightlife.lovePotion.value;
outputNightlife = "<a name='nightlife'></a>\n<div id='easy'>\n<h2>Nightlife</h2>\n
<table class='ntlf'><tr><th>Traits:</th><td class='white'>"+traits+"
</td></tr><tr><th>Turn Ons:</th><td class='white'>"+turnOnsOutput+"</td></tr><tr><th>
Turn Offs:</th><td class='white'>"+turnOff+"</td></tr></table>\n<p class='up2'>Perfume
Duration: <span class='colorme'>"+perfumeDuration+"</span></p>\n<p>Love Potion Duration:
<span class='colorme'>"+lovePotion+"</span></p>\n</div>\n"
document.inputNightlife.source8.value = outputNightlife;
return outputNightlife;
}
When I test it with chrome it says it cannot set property of .innerText of null which I think is because I don't want it to go to a div. I would like the value returned back to function generateNightlife so that it can be added to the outputNightlife. I don't know how to do that, and I need some help.
Here's a fully worked example that will pull multiple selections from a select element, before going on to construct an array with them, and finally printing them to screen.
Either of the two tutes you linked to are okay - it's always hard to know what will be obvious, what will need explaining and what will rely on background information that may/may not have already been covered.
I've used a few different tricks here and there are many that are more sophisticated I've elected to eschew. I hope the comments make the operation clear, though would be happy to add clarification as needed. :)
Here's a runnable snippet:
function byId(idStr){return document.getElementById(idStr);}
function getFormValues()
{
// 1. get a reference to each of the select elemenets we wish to process
var mainMealSelectElem = byId('mainSelect');
var dessertSelectElem = byId('dessertSelect');
// 2. get an array of all of the selected options in each of our select elements
var chosenMains = getSelectedOptions(mainMealSelectElem);
var chosenSweets = getSelectedOptions(dessertSelectElem);
var i, n, outputStr;
n = chosenMains.length;
outputStr = '';
for (i=0; i<n; i++)
{
// only add a comma before an element if at least one element already exists
// this is how we do it when writing a list manually.
if (outputStr != '')
outputStr += ", ";
// grab the two values from the array we constructed using the getSelectedOptions function.
// we said that each array element would have 2 fields, and named them "value" and "textLabel" - both entirely arbitrary name.
// whatever we named them in the below function is what we need to use to access them here.
outputStr += chosenMains[i].textLabel + " (" + chosenMains[i].value + ")";
}
// set the text content of the target span with the array of chosen stuff.
byId('mainsOutput').innerText = outputStr;
n = chosenSweets.length;
outputStr = '';
for (i=0; i<n; i++)
{
if (outputStr != '')
outputStr += ", ";
outputStr += chosenSweets[i].textLabel + " (" + chosenSweets[i].value + ")";
}
byId('dessertsOutput').innerText = outputStr;
}
// returns an array that consists of <value, text-label> pairs - 1 element for each selected option.
function getSelectedOptions(selectElem)
{
// aloop counter and the total number of iterations required
var i, nOptions = selectElem.options.length;
// the empty result array
var result = [];
// loop through all the options this select element has
for (i=0; i<nOptions; i++)
{
// if the current option is selected, we'll need to extract it's info and add it to the output array
if (selectElem.options[i].selected)
{
result.push(
{
value: selectElem.options[i].value,
textLabel: selectElem.options[i].label
}
);
}
}
return result;
}
div
{
display: inline-block;
}
.centered
{
text-align: center;
}
<div class='centered'>
<form>
<h2>Select the ones you like</h2>
<select id='mainSelect' multiple>
<option value='spag'>Spaghetti</option>
<option value='satay'>Peanut satay</option>
<option value='schnitz'>Chicken Schnitzel</option>
</select>
<select id='dessertSelect' multiple>
<option value='1'>Ice-cream</option>
<option value='2'>Fruit salad</option>
<option value='3'>Custard</option>
</select>
</form>
<br>
<button onclick='getFormValues()'>Get chosen values</button>
<hr>
</div>
<br>
<div>
Selected main-meals: <span id='mainsOutput'></span><br>
Selected desserts: <span id='dessertsOutput'></span><br>
</div>
And here's the full (copy/pastable) source:
<!doctype html>
<html>
<head>
<script>
function byId(idStr){return document.getElementById(idStr);}
function getFormValues()
{
// 1. get a reference to each of the select elemenets we wish to process
var mainMealSelectElem = byId('mainSelect');
var dessertSelectElem = byId('dessertSelect');
// 2. get an array of all of the selected options in each of our select elements
var chosenMains = getSelectedOptions(mainMealSelectElem);
var chosenSweets = getSelectedOptions(dessertSelectElem);
var i, n, outputStr;
n = chosenMains.length;
outputStr = '';
for (i=0; i<n; i++)
{
// only add a comma before an element if at least one element already exists
// this is how we do it when writing a list manually.
if (outputStr != '')
outputStr += ", ";
// grab the two values from the array we constructed using the getSelectedOptions function.
// we said that each array element would have 2 fields, and named them "value" and "textLabel" - both entirely arbitrary name.
// whatever we named them in the below function is what we need to use to access them here.
outputStr += chosenMains[i].textLabel + " (" + chosenMains[i].value + ")";
}
// set the text content of the target span with the array of chosen stuff.
byId('mainsOutput').innerText = outputStr;
n = chosenSweets.length;
outputStr = '';
for (i=0; i<n; i++)
{
if (outputStr != '')
outputStr += ", ";
outputStr += chosenSweets[i].textLabel + " (" + chosenSweets[i].value + ")";
}
byId('dessertsOutput').innerText = outputStr;
}
// returns an array that consists of <value, text-label> pairs - 1 element for each selected option.
function getSelectedOptions(selectElem)
{
// aloop counter and the total number of iterations required
var i, nOptions = selectElem.options.length;
// the empty result array
var result = [];
// loop through all the options this select element has
for (i=0; i<nOptions; i++)
{
// if the current option is selected, we'll need to extract it's info and add it to the output array
if (selectElem.options[i].selected)
{
result.push(
{
value: selectElem.options[i].value,
textLabel: selectElem.options[i].label
}
);
}
}
return result;
}
</script>
<style>
div
{
display: inline-block;
}
.centered
{
text-align: center;
}
</style>
</head>
<body>
<div class='centered'>
<form>
<h2>Select the ones you like</h2>
<select id='mainSelect' multiple>
<option value='spag'>Spaghetti</option>
<option value='satay'>Peanut satay</option>
<option value='schnitz'>Chicken Schnitzel</option>
</select>
<select id='dessertSelect' multiple>
<option value='1'>Ice-cream</option>
<option value='2'>Fruit salad</option>
<option value='3'>Custard</option>
</select>
</form>
<br>
<button onclick='getFormValues()'>Get chosen values</button>
<hr>
</div>
<br>
<div>
Selected main-meals: <span id='mainsOutput'></span><br>
Selected desserts: <span id='dessertsOutput'></span><br>
</div>
</body>
</html>
Related
I am a beginner in javascript, I am learning arrays. I am working on creating a html interface with javascript to use parallel arrays to obtain a users name and numeric value for each user (Score) I am stuck on understanding how I can save users input in each of the new arrays I created for each input. I have a button to save each name and score entry then I want to create a summary output that will check each score input and pass it through a loop to assign it a category such as A, B, C. I haven't gotten that far as I am confused on how to store each input in their array. The examples provided to me and the ones I found use predetermined values vs user input. This is what I have so far.
<h1>Grades</h1>
</header>
<br>
<p><b>Student Name:</b></p>
<input id="inp" type="text">
<br>
<br>
<p><b>Test Score:</b></p>
<input id="inps" type="text">
<br>
<br>
<br>
<button type="button" onclick="enter()">Enter</button>
<br>
<br>
<button type="button" onclick="summ()">Summary</button>
<br>
<p id="iop"></p>
<br>
<script>
var studentArr = new Array();
var scoreArr = new Array();
function enter() {
var studentName = document.getElementById("inp").value;
studentArr.push(inp);
var stuval = "";
for(i=0; i < studentArr.length; i++)
{
stuval = stuval + studentArr[i] + "<br/>";
}
document.getElementById("iop").innerHTML = stuval;
var studentScore = document.getElementById("inps").value;
scoreArr.push(inps);
var scoreval = "";
for(i=0; i < scoreArr.length; i++)
{
scoreval = scoreval + scoreArr[i] + "<br/>";
}
}
</script>
I belive more easier way exists:
var students = new Array();
function enter() {
students.push({
name: document.getElementById("inp").value,
score: document.getElementById("inps").value
});
show();
}
function show() {
document.getElementById("iop").innerHTML = "";
students.forEach(x => {
document.getElementById("iop").innerHTML += x.name + "<br/>";
});
}
You aren't using the right variable when pushing to your array here
studentArr.push(inp);
and here
scoreArr.push(inps);
Those variables do not exist in your code. You've defined 'studentName' and 'studentScore' so use them and you should have some data in your arrays.
How to sort list items by priority? This is a to-do list. User can input an item, choose a priority, and add to list.
This is my HTML form:
<input id="task" type="text"/>
<select id="priority">
<option id="Normal">Normal</option>
<option id="Urgent">Urgent</option>
<option id="Critical">Critical</option>
<option id="If You Can">If You Can</option>
</select>
<button onclick="amitFunction()">Add</button>
<hr/>
<table>
<tr>
<th id="result"></th>
<th id="priorit"></th>
</tr>
<table>
This is my JS:
function amitFunction() {
/* Define vars and values */
var lia = document.createElement("p");
var lib = document.createElement("p");
var item = document.getElementById('task').value;
var pro = document.getElementById('priority').value;
var pro_array = ['Urgent','Critical','Normal'];
var item_list = document.createTextNode(item);
var item_pro = document.createTextNode(pro);
lia.appendChild(item_list);
lib.appendChild(item_pro);
/* Check if text is less than 6 chars or more than 42 chars */
if (item.length<6) {
alert('Your text must have a least 6 chars');
} else if (item.length>42) {
alert('Your text must have less than 42 chars');
} else {
document.getElementById("result").appendChild(lia);
document.getElementById("priorit").appendChild(lib);
document.getElementById('task').value='';
}
/* Change text color base on priority */
if (pro==pro_array[0]) {
$("p:last-child").css('color','red');
}
if (pro==pro_array[1]) {
$("p:last-child").css('color','orange');
}
if (pro==pro_array[2]) {
$("p:last-child").css('color','green');
}
/* Delete text when user clicks on it */
$([lia,lib]).click(function(){
$([lia,lib]).css('color','gray');
$([lia,lib]).css("text-decoration", "line-through");
});
}
What I need is, when user adds a new item, it will sort by priority order.
first : Urgent
second : Critical
third : Normal
fourth : If You Can
Each new item that user adds, should be sorted like that. How can I do that?
This is the complete script (JSBin) to understand what I need.
First of all I would suggest you create a new table row every time you create a TODO task, however I decided to keep as much of your code as I could and implement what you asked for. I will admit that it is not the best decision and could be optimized a lot, however I am leaving it as it is simply because there might be many interesting cases in the code that might teach you something new. The sorting is implemented. I hope this helps :)
Your html, left as it was:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<input id="task" type="text"/>
<select id="priority">
<option id="Normal">Normal</option>
<option id="Urgent">Urgent</option>
<option id="Critical">Critical</option>
<option id="If You Can">If You Can</option>
</select>
<button onclick="amitFunction()">Add</button>
<hr/>
<table>
<tr>
<th id="result"></th>
<th id="priorit"></th>
</tr>
<table>
</body>
</html>
and the edited JS code:
//creating a global collection to hold your todo list in memory
var todo_list = [];
function amitFunction() {
var item = document.getElementById('task').value;
/* Check if text is less than 6 chars or more than 42 chars
and return if validation is not passed */
if(item.length<6){
alert('Your text must have a least 6 chars');
return;
}else if(item.length>42){
alert('Your text must have less than 42 chars');
return;
}
var pro = document.getElementById('priority').value;
//keep this for colors
var pro_array = ['Urgent','Critical','Normal'];
//map string priorities to numeric values
var priorities =
{
'Urgent' : 0,
'Critical' : 1,
'Normal' : 2,
'If You Can' : 3
}
//push each new task in the todo list
todo_list.push(
{
priority : pro,
task : item
}
);
//Now this here is perhaps the most important part,
//this is where you sort your todo list based on the
//mapped to numeric values priorities
todo_list.sort(function (task1, task2) {
return priorities[task1.priority] - priorities[task2.priority];
});
//clear the containers holding your list
var resultNode = document.getElementById("result");
while (resultNode.firstChild) {
resultNode.removeChild(resultNode.firstChild);
}
var priorityNode = document.getElementById("priorit");
while (priorityNode.firstChild) {
priorityNode.removeChild(priorityNode.firstChild);
}
//recreate the DOM based on the todo_list collection
for(var i =0; i < todo_list.length; i++)
{
var lia = document.createElement("p");
var lib = document.createElement("p");
var item_list = document.createTextNode(todo_list[i].task);
var item_pro = document.createTextNode(todo_list[i].priority);
lia.appendChild(item_list);
lib.appendChild(item_pro);
document.getElementById("result").appendChild(lia);
document.getElementById("priorit").appendChild(lib);
document.getElementById('task').value='';
/* Change text color base on priority */
if(todo_list[i].priority == pro_array[0]){
$("p:last-child").css('color','red');
}
if(todo_list[i].priority == pro_array[1]){
$("p:last-child").css('color','orange');
}
if(todo_list[i].priority == pro_array[2]){
$("p:last-child").css('color','green');
}
}
//reinitialize the click handlers
var resultNode = document.getElementById("result");
var priorityNode = document.getElementById("priorit");
for(var i =0; i< resultNode.childNodes.length; i++) (function(i){
resultNode.childNodes[i].onclick = function() {
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css('color','gray');
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css("text-decoration", "line-through");
}
priorityNode.childNodes[i].onclick = function() {
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css('color','gray');
$([resultNode.childNodes[i],priorityNode.childNodes[i]]).css("text-decoration", "line-through");
}
})(i);
}
And a working example here:
https://jsbin.com/kudipacexi/edit?html,js,output
In fact there are plenty of approaches, another approach would be to not keep a global collection for your list, instead do the sorting directly using the DOM elements, however you will still have to keep some kind of numeric representation of your priorities in order to sort them by priority. It might also be a good idea to subscribe each of the elements to a single click handler function, then add the line-through style based on the caller of the function. Another thing I'd suggest is, if you are involving jQuery and not focusing on just Vanilla JS, try and use jQuery for the majority of the DOM manipulation.
Because you have a table I suggest to use that as the output.
You can rearrange your select in order to add for each option a priority number and a color attribute like:
<option value="2" color="green">Normal</option>
The table can contain a first column as the current row priority. This column will be hidden.
Each time a new row must be added a sorting process is executed on table rows.
The snippet:
$('button').on('click', function (e) {
var priorityValue = $('#priority option:selected').val();
var priorityText = $('#priority option:selected').text();
var colorVal = $('#priority option:selected').attr('color');
var taskValue = $('#task').val();
if (taskValue.length < 6) {
$('#errMsg').text('Your text must have a least 6 chars');
return;
} else if (taskValue.length > 42) {
$('#errMsg').text('Your text must have less than 42 chars');
return;
}
$('#errMsg').text('');
//
// create the new table row...
//
var newRow = $('<tr/>', {style: 'color:' + colorVal})
.append($('<td/>', {style: "display: none", text: priorityValue}))
.append($('<td/>', {text: taskValue}))
.append($('<td/>', {text: priorityText}));
//
// enlarge current table rows with the current one and sort elements
//
var tableRowsSorted = $('#result tbody').append(newRow).find('tr').get().sort(function(a, b) {
var p1 = +$(a).find('td:first').text();
var p2 = +$(b).find('td:first').text();
return p1 - p2;
});
//
// append/replace the taable body
//
$('#result tbody').append(tableRowsSorted);
//
// reset input text
//
$('#task').val('');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="task" type="text"/><span id="errMsg" style="color: red;"></span>
<select id="priority">
<option value="2" color="green">Normal</option>
<option value="0" color="red">Urgent</option>
<option value="1" color="orange">Critical</option>
<option value="3" color="black">If You Can</option>
</select>
<button>Add</button>
<hr/>
<table id="result">
<thead>
<tr>
<th style="display: none">priority</th>
<th>result</th>
<th>priority</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
I'm creating a Time table generating website as a part of my project and I am stuck at one point.
Using for loop, I am generating user selected text boxes for subjects and faculties. Now the problem is that I cannot get the values of those dynamically generated text boxes. I want to get the values and store it into array so that I can then later on store it to database
If I am using localstorage, then it sometimes shows NaN or undefined. Please help me out.
Following is my Jquery code
$.fn.CreateDynamicTextBoxes = function()
{
$('#DynamicTextBoxContainer, #DynamicTextBoxContainer2').css('display','block');
InputtedValue = $('#SemesterSubjectsSelection').val();
SubjectsNames = [];
for (i = 0; i < InputtedValue; i++)
{
TextBoxContainer1 = $('#DynamicTextBoxContainer');
TextBoxContainer2 = $('#DynamicTextBoxContainer2');
$('<input type="text" class="InputBoxes" id="SubjectTextBoxes'+i+'" placeholder="Subject '+i+' Name" style="margin:5px;" value=""><br>').appendTo(TextBoxContainer1);
$('<input type="text" class="InputBoxes" id="FacultyTextBoxes'+i+'" placeholder="Subject '+i+' Faculty Name" style="margin:5px;" value=""><br>').appendTo(TextBoxContainer2);
SubjectsNames['SubjectTextBoxes'+i];
}
$('#DynamicTextBoxContainer, #UnusedContainer, #DynamicTextBoxContainer2').css('border-top','1px solid #DDD');
}
$.fn.CreateTimeTable = function()
{
for (x = 0; x < i; x++)
{
localStorage.setItem("Main"+x, +SubjectsNames[i]);
}
}
I am also posting screenshot for better understanding
I understand you create 2 text boxes for each subject, one for subject, and second one for faculty. And you want it as a jQuery plugin.
First of all, I think you should create single plugin instead of two, and expose what you need from the plugin.
You should avoid global variables, right now you have InputtedValue, i, SubjectsNames, etc. declared as a global variables, and I believe you should not do that, but keep these variables inside you plugin and expose only what you really need.
You declare your SubjectNames, but later in first for loop you try to access its properties, and actually do nothing with this. In second for loop you try to access it as an array, but it's empty, as you did not assign any values in it.
Take a look at the snippet I created. I do not play much with jQuery, and especially with custom plugins, so the code is not perfect and can be optimized, but I believe it shows the idea. I pass some selectors as in configuration object to make it more reusable. I added 2 buttons to make it more "playable", but you can change it as you prefer. Prepare button creates your dynamic text boxes, and button Generate takes their values and "print" them in result div. generate method is exposed from the plugin to take the values outside the plugin, so you can do it whatever you want with them (e.g. store them in local storage).
$(function() {
$.fn.timeTables = function(config) {
// prepare variables with jQuery objects, based on selectors provided in config object
var numberOfSubjectsTextBox = $(config.numberOfSubjects);
var subjectsDiv = $(config.subjects);
var facultiesDiv = $(config.faculties);
var prepareButton = $(config.prepareButton);
var numberOfSubjects = 0;
prepareButton.click(function() {
// read number of subjects from the textbox - some validation should be added here
numberOfSubjects = +numberOfSubjectsTextBox.val();
// clear subjects and faculties div from any text boxes there
subjectsDiv.empty();
facultiesDiv.empty();
// create new text boxes for each subject and append them to proper div
// TODO: these inputs could be stored in arrays and used later
for (var i = 0; i < numberOfSubjects; i++) {
$('<input type="text" placeholder="Subject ' + i + '" />').appendTo(subjectsDiv);
$('<input type="text" placeholder="Faculty ' + i + '" />').appendTo(facultiesDiv);
}
});
function generate() {
// prepare result array
var result = [];
// get all text boxes from subjects and faculties divs
var subjectTextBoxes = subjectsDiv.find('input');
var facultiesTextBoxes = facultiesDiv.find('input');
// read subject and faculty for each subject - numberOfSubjects variable stores proper value
for (var i = 0; i < numberOfSubjects; i++) {
result.push({
subject: $(subjectTextBoxes[i]).val(),
faculty: $(facultiesTextBoxes[i]).val()
});
}
return result;
}
// expose generate function outside the plugin
return {
generate: generate
};
};
var tt = $('#container').timeTables({
numberOfSubjects: '#numberOfSubjects',
subjects: '#subjects',
faculties: '#faculties',
prepareButton: '#prepare'
});
$('#generate').click(function() {
// generate result and 'print' it to result div
var times = tt.generate();
var result = $('#result');
result.empty();
for (var i = 0; i < times.length; i++) {
$('<div>' + times[i].subject + ': ' + times[i].faculty + '</div>').appendTo(result);
}
});
});
#content div {
float: left;
}
#content div input {
display: block;
}
#footer {
clear: both;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div id="header">
<input type="text" id="numberOfSubjects" placeholder="Number of subjects" />
<button id="prepare">
Prepare
</button>
</div>
<div id="content">
<div id="subjects">
</div>
<div id="faculties">
</div>
</div>
</div>
<div id="footer">
<button id="generate">Generate</button>
<div id="result">
</div>
</div>
folks! Today I created this script that has the following functionality:
add new items to array
list all items from the array
remove an item from the array
There are two functions:
addToFood() - adds the value of input to the array and updates
innerHTML of div
removeRecord(i) - remove a record from the array and updates
innerHTML of div
The code includes 3 for loops and you can see it at - http://jsfiddle.net/menian/3b4qp/1/
My Master told me that those 3 for loops make the solution way to heavy. Is there a better way to do the same thing? Is it better to decrease the loops and try to use splice? Thanks in advance.
HTML
<!-- we add to our foodList from the value of the following input -->
<input type="text" value="food" id="addFood" />
<!-- we call addToFood(); through the following button -->
<input type="submit" value="Add more to food" onClick="addToFood();">
<!-- The list of food is displayed in the following div -->
<div id="foods"></div>
JavaScript
var foodList = [];
function addToFood () {
var addFood = document.getElementById('addFood').value;
foodList.push(addFood);
for (i = 0; i < foodList.length; i++) {
var newFood = "<a href='#' onClick='removeRecord(" + i + ");'>X</a> " + foodList[i] + " <br>";
};
document.getElementById('foods').innerHTML += newFood;
}
function removeRecord (i) {
// define variable j with equal to the number we got from removeRecord
var j = i;
// define and create a new temporary array
var tempList = [];
// empty newFood
// at the end of the function we "refill" it with the new content
var newFood = "";
for (var i = 0; i < foodList.length; i++) {
if(i != j) {
// we add all records except the one == to j to the new array
// the record eual to j is the one we've clicked on X to remove
tempList.push(foodList[i]);
}
};
// make redefine foodList by making it equal to the tempList array
// it should be smaller with one record
foodList = tempList;
// re-display the records from foodList the same way we did it in addToFood()
for (var i = 0; i < foodList.length; i++) {
newFood += "<a href='#' onClick='removeRecord(" + i + ");'>X</a> " + foodList[i] + " <br>";
};
document.getElementById('foods').innerHTML = newFood;
}
You should use array.splice(position,nbItems)
function removeRecord (i) {
foodList.splice(i, 1); // remove element at position i
var newFood = "";
for (var i = 0; i < foodList.length; i++) {
newFood += "<a href='#' onClick='removeRecord(" + i + ");'>X</a> "
+ foodList[i] + " <br>";
};
document.getElementById('foods').innerHTML = newFood;
}
http://jsfiddle.net/3b4qp/5/
Now using JQuery:
$(function(){
$(document).on('click','input[type=submit]',function(){
$('#foods')
.append('<div>X '
+ $('#addFood').val() + '</div>');
});
$(document).on('click','.item',function(){
$(this).parent().remove();
});
});
http://jsfiddle.net/jfWa3/
Your problem isn't the arrays, your problem is this code:
node.innerHTML += newFood;
This code is very, very, very slow. It will traverse all exising DOM nodes, create strings from them, join those strings into one long string, append a new string, parse the result to a new tree of DOM nodes.
I suggest to use a framework like jQuery which has methods to append HTML fragments to existing DOM nodes:
var parent = $('#foods');
...
for (var i = 0; i < foodList.length; i++) {
parent.append( "<a href='#' onClick='removeReco..." );
That will parse the HTML fragments only once.
If you really must do it manually, then collect all the HTML in a local string variable (as suggested by JohnJohnGa in his answer) and then assign innerHTML once.
Here's some tips to, at least, make your code more portable (dunno if it will be better performance wise, but should be, since DOM Manipulation is less expensive)
Tips
First separate your event handle from the HTML
Pass the "new food" as a function paramater
Tie the array elements to the DOM using the ID
Instead of rerendering everything when something changes (using innerHTML in the list), just change the relevant bit
Benefits:
You actually only loop once (when removing elements from the array).
You don't re-render the list everytime something changes, just the element clicked
Added bonus: It's more portable.
Should be faster
Example code:
FIDDLE
HTML
<div id="eventBinder">
<!-- we add to our foodList from the value of the following input -->
<input id="addFood" type="text" value="food" />
<!-- we call addToFood(); through the following button -->
<button id="addFoodBtn" value="Add more to food">Add Food</button>
<!-- The list of food is displayed in the following div
-->
<div id="foods"></div>
</div>
JS
// FoodList Class
var FoodList = function (selectorID) {
return {
foodArray: [],
listEl: document.getElementById(selectorID),
idCnt: 0,
add: function (newFood) {
var id = 'myfood-' + this.idCnt;
this.foodArray.push({
id: id,
food: newFood
});
var foodDom = document.createElement('div'),
foodText = document.createTextNode(newFood);
foodDom.setAttribute('id', id);
foodDom.setAttribute('class', 'aFood');
foodDom.appendChild(foodText);
this.listEl.appendChild(foodDom);
++this.idCnt;
},
remove: function (foodID) {
for (var f in this.foodArray) {
if (this.foodArray[f].id === foodID) {
delete this.foodArray[f];
var delFood = document.getElementById(foodID);
this.listEl.removeChild(delFood);
}
}
}
};
};
//Actual app
window.myFoodList = new FoodList('foods');
document.getElementById('eventBinder').addEventListener('click', function (e) {
if (e.target.id === 'addFoodBtn') {
var food = document.getElementById('addFood').value;
window.myFoodList.add(food);
} else if (e.target.className === 'aFood') {
window.myFoodList.remove(e.target.id);
}
}, false);
Here is another sugestion:
function remove(arr, index) {
if (index >= arr.lenght) { return undefined; }
if (index == 0) {
arr.shift();
return arr;
}
if (index == arr.length - 1) {
arr.pop();
return arr;
}
var newarray = arr.splice(0, index);
return newarray.concat(arr.splice(1,arr.length))
}
Im trying to build a form that calculates a total price based on a series of drop down boxes with string values such as "This option costs £30" i know this is not ideal but im putting this together as a hack for an existing script
For the most part ive got it working however im not sure how to run the each function for each child of #productconfig
I can manually input each of the drop downs ids into an array and that makes the calculation but it would be good if it just worked with all the children of #productconfig
<code>
<div id="#productconfig">
<label>Model Type</label>
<select name="products[220][data][modeltype]" id="data-modeltype-220">
<option value="M-Type £500">M-Type £500</option>
<option value="P-Type £500">P-Type £500</option>
<option value="S-Type £500">S-Type £500</option>
</select>
</div>
</code>
<code>
$(document).ready(function() {
$("#productconfig").children().change(function () {
calculateoptions();
});
calculateoptions();
});
</code>
<code>
function calculateoptions() {
var arr = ["data-modeltype-220"];
var total = 0;
jQuery.each(arr, function () {
var str = $('#' + this).attr("value");
var poundsign = str.indexOf('£');
var poundsign = poundsign + 1;
var lengthofstr = str.length;
var shortstr = str.substr(poundsign, lengthofstr);
total = eval(total) + eval(shortstr);
});
$('#price').html("£" + total);
}
</code>
How about this:
function calculateoptions() {
var total = 0;
jQuery('#productconfig select').each(function () {
total += $(this).val().match(/£(\d+)/)[1];
});
$('#price').html("£" + total);
}
You can use:
$("#productconfig select").each(function(){...});
To select each drop down in the product config div.