I'm writing a little exercise where I move things from one "select" to another. Works great! I don't have multi-select yet, but I'll do that later. More importantly, I thought I would add counters so that values associated with the "options" could be added up. That function doesn't seem to be working. When I move things from one column to the other the labels meant for the summed values are filled with the text 0[object Object]. I'm sure this is on account of a pretty obvious mistake, but I lack the vocabulary to articulate the problem in any other terms. Please teach me? :-)
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<style type="text/css">
#selectSource {
width: 320px;
}
#selectTarget {
width: 320px;
}
#wgtsum {
width: 22px;
}
#cubesum {
width: 22px;
}
</style>
<title>Loader</title>
<script>
var data;
var sumCube = 0;
var sumWgt = 0;
window.onload = function () {
//Add way to load JSON data from a text file or something
var load = document.getElementById('selectSource')
data = [
{ num: "1", cube: 6, wgt: 2, title: "num: 1 cube: 6 wgt: 2" },
{ num: "2", cube: 4, wgt: 4, title: "num: 2 cube: 4 wgt: 4" },
{ num: "3", cube: 2, wgt: 6, title: "num: 3 cube: 2 wgt: 6" }
];
for (i = 0; i < data.length; i++) {
load.options.add(new Option(data[i].title, data[i].num));
}
}
function sum() {
var sumTarget = document.getElementById('selectTarget');
for (i = 0; i < data.length; i++) {
for (o = 0; o < sumTarget.length; o++) {
if (sumTarget[o].value = data[i].value) {
sumCube = sumCube + data[i].cube;
sumWgt = sumWgt + data[i].wgt;
}
}
}
document.getElementById("cubesum").setAttribute("value", sumCube);
document.getElementById("wgtsum").setAttribute("value", sumWgt);
}
function move(from, to) {
selected = from.options.selectedIndex;
to.options.add(new Option(from.options[selected].text, from.options[selected].value));
from.options.remove(selected);
sum();
}
</script>
</head>
<body>
<form>
<select id="selectTarget" size="4">
</select>
<button type="button" onclick="move(this.form.selectSource, this.form.selectTarget)" style="width: 58px">To Target</button>
<button type="button" onclick="move(this.form.selectTarget, this.form.selectSource)" style="width: 58px">To Source</button>
<select id="selectSource" size="4">
</select>
<input value="0" type="text" id="cubesum" size="5"/>
<input value="0" type="text" id="wgtsum" size="5"/>
</form>
</body>
</html>
In your "sum() " function it looks like your if statement should be doing a comparison == instead of an assignment =.
Related
This question already has answers here:
How to sort an array of integers correctly
(32 answers)
Closed 4 months ago.
Can anyone help me to find the error? My main target is when we enter an array to give the result same as the below I showed, Input
{-5, -4, 10, 12, 13, 4, 5, -8, -6}
output
{10, 12, 13, 4, 5, -5, -4, -8, -6}
Here I attached my full code. I don't get outputs.
function foo() {
var arrch = document.getElementById("text").value.spilt(" ").map(Number);
var dlina = arrch.length;
var new_arr = [];
var new_arr2 = [];
var k = 0;
var summa = 0;
var n = 0;
for (var i = 0; i < dlina; i++) {
if (arrch[i] > 0) {
summa += arrch[i];
new_arr[n] = arrch[i];
n++;
} else if (arrch[i] < 0) {
new_arr2[k] = arrch[i];
k++;
}
}
new_arr = new_arr.join(" ");
new_arr2 = new_arr2.join(" ");
document.getElementById("text2").innerHTML = new_arr + " " + new_arr2;
document.getElementById("text3").innerHTML = summa;
}
<H3>RK1</H3>
<form>
<p>Enter Array</p>
<input type="text" id="text" placeholder="Enter Text"><br>
<p>Changed array</p>
<p id="text2"></p>
<p>plus Array</p>
<p id="text3"></p>
<button onclick="foo()">Give Answer</button>
</form>
If you just want to sort the negative numbers to the end of the array, you can sort based on the sign of the numbers. You can sum the array using reduce:
const arr = [-5,-4,10,12,13,4,5,-8,-6]
arr.sort((a, b) => Math.sign(b) - Math.sign(a))
console.log(arr)
const sum = arr.reduce((acc, el) => acc + el)
console.log(sum)
Here's a modified version of your code with those changes (and the typo in split noted by #EricFortis corrected):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--
Positive Numbers one side and negetive numbers other side
-->
<title>RK1</title>
</head>
<body>
<H3>RK1</H3>
<form>
<p>Enter Array</p>
<input type="text" id="text" placeholder="Enter Text"><br>
<p>Changed array</p>
<p id="text2"></p>
<p>plus Array</p>
<p id="text3"></p>
<button onclick="foo(event)">Give Answer</button>
</form>
<script type="text/javascript">
function foo(e) {
e.preventDefault();
var arrch = document.getElementById("text").value.split(" ").map(Number);
arrch.sort((a, b) => Math.sign(b) - Math.sign(a))
var summa = arrch.reduce((acc, el) => acc + el)
document.getElementById("text2").innerText = arrch;
document.getElementById("text3").innerText = summa;
}
</script>
</body>
</html>
you can code that this way...
const myForm = document.querySelector('#my-form')
myForm.onsubmit = e =>
{
e.preventDefault()
let res = myForm.txtNums.value
.match(/([-+]?\d+)/g).map(Number)
.reduce((r,n) =>
{
r[(n<0?'neg':'pos')].push(n)
r.sum += n
return r
}
,{sum:0,pos:[],neg:[]}
)
myForm.txtOut_1.textContent = `{${res.pos.concat(res.neg).join(',')}}`
myForm.txtOut_2.textContent = res.sum
}
body {
font-family : Arial, Helvetica, sans-serif;
font-size : 16px;
}
label {
display : block;
font-size : .8rem;
padding : .4rem .2rem;
}
input,
output {
display : block;
border : 1px solid lightslategrey;
width : 16rem;
font-size : 1rem;
padding : .2rem .4em;
}
<form id="my-form">
<label>Enter Array
<input type="text" name="txtNums"
placeholder="Enter Text"
value="{-5,-4,10,12,13,4,5,-8,-6}">
</label>
<label>Changed array
<output name="txtOut_1">.</output>
</label>
<label>plus Array
<output name="txtOut_2">.</output>
</label>
<button>Give Answer</button>
</form>
I've looked into the stackoverflow recommendations about posts with similar titles and nothing solved my issue. Im new to JS, im trying to make a simple multiple choice quiz. If you get it correct you score a point, if not, u don't get any but the question counter increases, the thing is after the first question everything adds double the value, here's a quick video showcasing de error: https://gyazo.com/9fbdf63508b2713992935d813f29788e pay attention to the bottom-right corner.
This is a side project and I've been stuck on it for almost 2 weeks now.. no joke. Any help is extremely appreciated, my main language is Spanish so that's the reason of the variables names. Here's the code:
let pregunta = document.getElementById('preguntafinal');
let puntaje = document.getElementById('puntaje');
let opcion1 = document.getElementById('opcion1');
let opcion2 = document.getElementById('opcion2');
let opcion3 = document.getElementById('opcion3');
let opcion4 = document.getElementById('opcion4');
let puntito1 = document.querySelector('puntito1');
let idPreg = 0;
let respSeleccionada;
let puntosTotales = 0;
let preguntasTotales = 0;
function iterarJuego() {
pregunta.innerText = arrayPreguntas[idPreg].preg;
opcion1.innerText = arrayPreguntas[idPreg].opcionuno;
opcion2.innerText = arrayPreguntas[idPreg].opciondos;
opcion3.innerText = arrayPreguntas[idPreg].opciontres;
opcion4.innerText = arrayPreguntas[idPreg].opcioncuatro;;
elegirRespuesta();
}
function elegirRespuesta() {
opcion1.addEventListener("click", asd => {
respSeleccionada = arrayPreguntas[idPreg].opcionuno;
funAnalizar(respSeleccionada);
});
opcion2.addEventListener("click", asd => {
respSeleccionada = arrayPreguntas[idPreg].opciondos;
funAnalizar(respSeleccionada);
});
opcion3.addEventListener("click", asd => {
respSeleccionada = arrayPreguntas[idPreg].opciontres;
funAnalizar(respSeleccionada);
});
opcion4.addEventListener("click", asd => {
respSeleccionada = arrayPreguntas[idPreg].opcioncuatro;
funAnalizar(respSeleccionada);
});
}
function funAnalizar() {
console.log(respSeleccionada);
console.log(arrayPreguntas[idPreg].error);
if (respSeleccionada == arrayPreguntas[idPreg].error) {
console.log("correcto");
respCorrecta(respSeleccionada);
} else if (respSeleccionada != arrayPreguntas[idPreg].error) {
console.log("incorrecto");
respIncorrecta(respSeleccionada);
}
}
function respCorrecta() {
puntosTotales++;
preguntasTotales++;
puntaje.innerText = puntosTotales + "/" + preguntasTotales;
idPreg++;
iterarJuego(idPreg);
console.log("ja");
}
function respIncorrecta() {
preguntasTotales++;
puntaje.innerText = puntosTotales + "/" + preguntasTotales;
idPreg++;
iterarJuego(idPreg);
console.log("jant");
}
arrayPreguntas = [{
idPreg: 0,
preg: "Que significa AI en Japonés?",
opcionuno: 'amor',
opciondos: 'carcel',
opciontres: 'pizza',
opcioncuatro: 'caja',
error: 'amor'
}, {
idPreg: 1,
preg: "Cual es el hiragana 'ME' ?",
opcionuno: 'ぬ',
opciondos: 'ね',
opciontres: 'ぐ',
opcioncuatro: 'め',
error: 'め'
}, {
idPreg: 2,
preg: "En hiragana: DESAYUNO , ALMUERZO , CENA ?",
opcionuno: 'ぬ',
opciondos: 'ね',
opciontres: 'ぐ',
opcioncuatro: 'め',
error: 'め'
}, {
idPreg: 3,
preg: "Como se dice madre y padre ?",
opcionuno: 'chichi hana',
opciondos: 'hana mitsu',
opciontres: 'kirei chichi',
opcioncuatro: 'undo chichi',
error: 'kirei chichi'
}, {
idPreg: 4,
preg: "Que significa きれい ?",
opcionuno: 'rey y reina',
opciondos: 'lindo y linda',
opciontres: 'hermoso y hermosa',
opcioncuatro: 'salvaje y saldro',
error: 'lindo y linda'
}]
iterarJuego();
That's the .js ..if for some reason the html is needed ill add it with an edit.
Redid everything into English, it was messy (BTW there's a button called "Tidy" in the editor) but I couldn't find the bug, I might've corrected it without being aware I had? Anyways, I liked the flow of the code so instead of giving up I made some improvements.
The terse syntax is from HTMLFormElement interface:
Figure I
// <form id="QA"> name attribute can be used as id
const QA = document.forms.QA;
// We can make a reference to all form controls in <form>
const IO = QA.elements;
// With that defined, referencing form controls is simple
IO.question // <output id='question'>
IO.next // <button id='next'>
The HTMLFormControlsCollection interface allows us to group form controls by a common name attribute and use array methods on them or extract a value of a checked or selected form control:
Figure II
// 1 of 4 <input id='optB' name='opt' type='radio' value='optB' checked>
IO.opt // HTMLCollection of all [name='opt']
[...IO.opt] // Array of all [name='opt']
IO.opt.value // Automatically gets the value of the checked [name='opt']
I replaced the 4 click handlers with one submit handler:
Figure III
// <form> is bound to the submit event
QA.onsubmit = evaluate;
function evaluate(e) {
e.preventDefault();
If you decide to handle the submit event remember to use e.preventDefault()
otherwise the will attempt to send data to a server, and the response
will kill the page.
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
html {
font: 300 3ch/1.2 'Segoe UI'
}
#score {
font-size: 1.25rem;
}
#score::before {
content: 'Score: '
}
ol {
list-style: lower-latin;
margin-top: 0
}
input,
button {
font: inherit
}
li {
margin-bottom: 8px;
}
button {
display: inline-flex;
align-items: center;
padding: 0 0.5rem;
cursor: pointer
}
</style>
</head>
<body>
<main>
<form id='QA'>
<fieldset>
<legend><output id='score'></output></legend>
<output id='question'></output>
<ol>
<li>
<input id='optA' name='opt' type='radio' value='optA'>
<label for='optA'></label>
</li>
<li>
<input id='optB' name='opt' type='radio' value='optB'>
<label for='optB'></label>
</li>
<li>
<input id='optC' name='opt' type='radio' value='optC'>
<label for='optC'></label>
</li>
<li>
<input id='optD' name='opt' type='radio' value='optD'>
<label for='optD'></label>
</li>
</ol>
<menu>
<button id='next'>Next</button>
</menu>
</fieldset>
</form>
</main>
<script>
const QA = document.forms.QA;
const IO = QA.elements;
const question = IO.question;
const score = IO.score;
const opt1 = IO.optA.nextElementSibling;
const opt2 = IO.optB.nextElementSibling;
const opt3 = IO.optC.nextElementSibling;
const opt4 = IO.optD.nextElementSibling;
let qID = 0;
let totalP = 0;
let totalQ = 0;
function quiz() {
question.textContent = (qID + 1) + '. ' + qArray[qID].ques;
opt1.textContent = qArray[qID].optA;
opt2.textContent = qArray[qID].optB;
opt3.textContent = qArray[qID].optC;
opt4.textContent = qArray[qID].optD;
[...IO.opt].forEach(o => {
if (o.checked) {
o.checked = false;
}
});
}
QA.onsubmit = evaluate;
function evaluate(e) {
e.preventDefault();
let selected = IO.opt.value;
if (selected === qArray[qID].right) {
correct();
} else {
wrong();
}
}
function correct() {
totalP++;
totalQ++;
score.textContent = totalP + " / " + totalQ;
qID++;
if (qID >= qArray.length) {
return IO.next.style.display = 'none';
}
quiz();
}
function wrong() {
totalQ++;
score.textContent = totalP + " / " + totalQ;
qID++;
if (qID >= qArray.length) {
return IO.next.style.display = 'none';
}
quiz();
}
qArray = [{
qID: 0,
ques: "Que significa AI en Japonés?",
optA: 'amor',
optB: 'carcel',
optC: 'pizza',
optD: 'caja',
right: 'optA'
}, {
qID: 1,
ques: "Cual es el hiragana 'ME' ?",
optA: 'ぬ',
optB: 'ね',
optC: 'ぐ',
optD: 'め',
right: 'optD'
}, {
qID: 2,
ques: "En hiragana: DESAYUNO , ALMUERZO , CENA ?",
optA: 'ぬ',
optB: 'ね',
optC: 'ぐ',
optD: 'め',
right: 'optB'
}, {
qID: 3,
ques: "Como se dice madre y padre ?",
optA: 'chichi hana',
optB: 'hana mitsu',
optC: 'kirei chichi',
optD: 'undo chichi',
right: 'optC'
}, {
qID: 4,
ques: "Que significa きれい ?",
optA: 'rey y reina',
optB: 'lindo y linda',
optC: 'hermoso y hermosa',
optD: 'salvaje y saldro',
right: 'optB'
}];
quiz();
</script>
</body>
</html>
Learning JS, trying to get this little survey to work, but I get a "function not defined" error at HTML element onclick. Can't figure out why, it seems like the function is defined and I can't find any syntax errors. I also get an error saying that "soda is not defined," but it seems like that variable is defined as an object in the array.
<html>
<body>
<h3>What cats are in the room?</h3>
Sophie: <input type="checkbox" id="sophieCheck">
<br></br>
Soda: <input type="checkbox" id="sodaCheck">
<br></br>
Mr.: <input type="checkbox" id="mrCheck">
<br></br>
<button onclick="catsInTheRoom()">Try it</button>
<br></br>
<p id="cats"></p>
<script>
function catsInTheRoom() {
var myCats = [ soda, mr, sophie ];
if (getElementById("sodaCheck").checked) {
myCats[0] = 1;
} else {
myCats[0] = 0;
}
if (getElementById("sophieCheck").checked) {
myCats[2] = 10;
} else {
myCats[2] = 0;
}
if (getElementById("mrCheck").checked) {
myCats[1] = 20;
} else {
myCats[1] = 0;
}
getElementById("cats").innerHTML = myCats[0] + myCats[1] + myCats[2];
}
</script>
</body>
</html>
I created the array so I can use the numerical values for a switch statement to produce different text based on which checkboxes are checked.
This simpler version worked:
Sophie: <input type="checkbox" id="sohpieCheck">
<br></br>
Soda: <input type="checkbox" id="sodaCheck">
<br></br>
Mr.: <input type="checkbox" id="mrCheck">
<br></br>
<button onclick="myFunction()">Try it</button>
<p id="cats"></p>
<script>
function myFunction() {
var soda = document.getElementById("sodaCheck").checked;
if (soda) {
catBehavior = "Cats are being rascals.";
} else {
catBehavior = "Cats are behaving nicely.";
}
document.getElementById("cats").innerHTML = catBehavior;
}
</script>
Why does the second example work, but not the first one?
script error: soda, mr, sophie are used as variables but are never defined before .. throws error and stops function execution.
var myCats = [ 0, 0, 0 ]; // will initialize myCats to 0,0,0
or you can defined variables first:
var soda = 0, mr = 0, sophie = 0;
bunt since you never use them after initial myCats definition, initializing to 0s should enough
Your code has some errors. Please, check the fixes:
<br> tags are self closed.
soda, mr and sophie must be strings.
getElementById belongs to the document object (or any other DOM node).
<h3>What cats are in the room?</h3>
Sophie: <input type="checkbox" id="sophieCheck">
<br>
Soda: <input type="checkbox" id="sodaCheck">
<br>
Mr.: <input type="checkbox" id="mrCheck">
<br>
<button onclick="catsInTheRoom()">Try it</button>
<br>
<p id="cats"></p>
<script>
function catsInTheRoom() {
var myCats = [ 'soda', 'mr', 'sophie' ];
if (document.getElementById("sodaCheck").checked) {
myCats[0] = 1;
} else {
myCats[0] = 0;
}
if (document.getElementById("sophieCheck").checked) {
myCats[2] = 10;
} else {
myCats[2] = 0;
}
if (document.getElementById("mrCheck").checked) {
myCats[1] = 20;
} else {
myCats[1] = 0;
}
document.getElementById("cats").innerHTML = myCats[0] + myCats[1] + myCats[2];
}
</script>
the line 'var myCats = [ soda, mr, sophie ];' is the cause of the error. You are using strings as variables. If you must use those strings, then create an object with those properties and initialize the values as follows;
var myCats ={soda=0, mr=0, sophie=0};
if (getElementById("sodaCheck").checked) {
myCats.soda = 1;
} else {
myCats.soda = 0;
}
if (getElementById("sophieCheck").checked) {
myCats.sophie= 10;
} else {
myCats.sophie = 0;
}
if (getElementById("mrCheck").checked) {
myCats.mr = 20;
} else {
myCats.mr = 0;
}
getElementById("cats").innerHTML = myCats.soda + myCats.mr + myCats.sophie;
}
I want to make a live search and then select those suggestions for further implementation!But it doesn't put values in the select field.In other words,option tag is not working!!!
here is the code!!!
//index.php
<html lang="en"><head>
<meta charset="utf-8">
<title>Live Search</title>
</head>
<body>
<div id="searcharea">
<label for="search">live search</label>
<p>Enter the name</p>
<input type="search" name="search" id="search" placeholder="name or info">
</div>
<div>
<div id="top"></div>
<div id="center"></div>
<div id="bottom"></div>
</div>
<script src="jquery-1.12.2.min.js"></script>
<script src="basic.js"></script>
</body>
</html>
//location.json
[
{
"name":"Barot Bellingham",
},
{
"name":"Jonathan G. Ferrar II",
},
{
"name":"Hillary Hewitt Goldwynn-Post",
},
{
"name":"Hassum Harrod",
},
{
"name":"Jennifer Jerome",
},
{
"name":"LaVonne L. LaRue",
},
{
"name":"Constance Olivia Smith",
},
{
"name":"Riley Rudolph Rewington",
},
{
"name":"Xhou Ta",
}
]
//basic.js
$('#search').keyup(function()
{
var searchField=$('#search').val();
var myExp=new RegExp(searchField,"i");
var slct_start='<select>';
$('#top').html(slct_start);
$.getJSON('location.json',function(data)
{
var output='<ul class="searchresults">';
$.each(data,function(key,val){
if(val.name.search(myExp)!=-1)
{
output='<option '+'value='+val.name+'>'+val.name+'</option>';
}
});
$('#center').html(output);
});//get json
var slct_end='</select>';
$('#bottom').html(slct_end);
});
Instead of this KeyUp function,
You can use the Jquery AutoComplete functionality for easy search.
Try that one.
Refer this AutoComplete search in Jquery
I found that your location.json's data is not standard, it should like this:
[
{
"name":"Barot Bellingham"
},
{
"name":"Jonathan G. Ferrar II"
},
{
"name":"Hillary Hewitt Goldwynn-Post"
},
{
"name":"Hassum Harrod"
},
{
"name":"Jennifer Jerome"
},
{
"name":"LaVonne L. LaRue"
},
{
"name":"Constance Olivia Smith"
},
{
"name":"Riley Rudolph Rewington"
},
{
"name":"Xhou Ta"
}
]
first we put a html file as index.html
<div class="container">
<h4 class="publish-work" >PUBLISHED WORKS AVAILABLE</h4>
<br>
<input type="search" class="search" name="search" >
<br>
<br>
<table id="datalist" class="table table-bordered" style="height: 400px; overflow-y: auto;">
<thead>
<th>S.no</th>
<th>Name</th>
<th>Price</th>
</thead>
<tbody>
</tbody>
</table>
then add a script.js file
$(document).ready(function()
{
var table_object = [];
for(var i=0; i<1500; i++)
{
var object1 = [''];
object1[0] = randomString() ;
object1[1] = i+1;
object1[2] = i*10/2;
table_object.push( object1 );
}
console.log( table_object );
function render ( arraylist ) {
var html = '';
for(var i=0;i<arraylist.length;i++)
{
//var html = '';
html += '<tr>';
html += '<td>'+arraylist[i][0]+'</td>';
html += '<td>'+arraylist[i][1]+'</td>';
html += '<td>'+arraylist[i][2]+'</td>';
html += '</tr>';
//html1 += html;
//console.log(html);
}
console.log(html);
$("#datalist tbody").html(html);
}
render( table_object );
$(".search").on("keyup", function() {
var value = $(this).val();
var anotherval = [''];
var dummyarray = [];
for ( i=0; i<table_object.length; i++ )
{
if ( table_object[i]["name"].indexOf(value) != -1 )
{
dummyarray.push(table_object[i]);
}
}
render(dummyarray);
});
function randomString ()
{
var randomStringArray = [];
var stringSet = "abcdefghijklmnopqrstuvwxyz";
for (var i = 0; i < 100; i++) {
var maxLength = Math.floor(Math.random() * (10 - 5 + 1)) + 5;
var String = "";
for (var j = 0; j < maxLength; j++) {
String += stringSet[Math.floor(Math.random() * stringSet.length)];
}
randomStringArray.push(String);
}
return String;
}
});
thats it..
Using knockout, I have a select (a list of names) whose options are bound to another set of knockout-bound data (people). When the name of any person changes, the value of the select option that is bound to that person's name is correctly updated. However, the select's selection is not preserved if you had that person selected already.
See this jsFiddle for a live example: http://jsfiddle.net/DbBZQ/
Select "Jane" from the list.
Change the name "Jane" to something else ("Jane Doe" for example).
Notice the select defaults back to the first item.
How can I make the selection stick to the same option index even if the underlying value has changed? Is there a way to instruct knockout to preserve the selection or do I have to do this separately using JS?
Complete Code Example
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var data =
{
people: ko.observableArray(
[
{ name: ko.observable("Jim") },
{ name: ko.observable("Jane") },
{
name: ko.observable("Sam"),
subordinates: ko.observableArray(
[
{
name: ko.observable("Tambone"),
subordinates: ko.observableArray(
[
{ name: ko.observable("Edward") },
{ name: ko.observable("Kristy") },
{ name: ko.observable("Thomas") },
{ name: ko.observable("Andy") }
])
},
{ name: ko.observable("Jules") }
])
}
])
};
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (name, indent)
{
var option =
{
value: name,
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}
</script>
</head>
<body>
<div data-bind="foreach: data.people">
<input type="text" data-bind="value: name" /><br />
</div>
Add Person
<br /><br /><br />
<select data-bind="options: allNames, optionsValue: 'value', optionsText: 'text', optionsCaption: 'All Names...'" />
<script type="text/javascript">
ko.applyBindings();
</script>
</body>
</html>
The reason the selection is lost is because the selected value is matched directly to the name property, which changes. As a result, the selected value no longer exists in the data source (allNames).
If you want to retain the selection, you have a couple of options:
Implement a hack such as tracking the index, and resetting it after the value changes
Bind the selected value to a property that doesn't change.
Do you have an immutable property that you can use as the selected value?
For the sake of an example, I added an id property to the objects in the data source, and use that as the selected value instead of name. This works the way you expect:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var data =
{
people: ko.observableArray(
[
{ id: 1, name: ko.observable("Jim") },
{ id: 2, name: ko.observable("Jane") },
{
id: 3, name: ko.observable("Sam"),
subordinates: ko.observableArray(
[
{
id: 4, name: ko.observable("Tambone"),
subordinates: ko.observableArray(
[
{ id: 5, name: ko.observable("Edward") },
{ id: 6, name: ko.observable("Kristy") },
{ id: 7, name: ko.observable("Thomas") },
{ id: 8, name: ko.observable("Andy") }
])
},
{ id: 9, name: ko.observable("Jules") }
])
}
])
};
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (id, name, indent)
{
var option =
{
value: id,
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i].id, data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i].id,subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}
</script>
</head>
<body>
<div data-bind="foreach: data.people">
<input type="text" data-bind="value: name" /><br />
</div>
Add Person
<br /><br /><br />
<select data-bind="options: allNames, optionsValue: 'value', optionsText: 'text', optionsCaption: 'All Names...'" />
<script type="text/javascript">
ko.applyBindings();
</script>
</body>
</html>
Edit:
As an alternative, what if you set up the value property so that it was a ko.computed that returned the index of the item? Like this:
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (item, name, indent)
{
var option =
{
value: ko.computed(function(){ return data.people().indexOf(item);}),
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i], data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i],subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}