Is there any plugin, preferably vanilla but jquery plugins work too, that can turn a JavaScript object into respective select elements.
obj = {
Ford: {
1: "Focus",
2: "Fiesta"
},
Toyota: {
1: "Prius",
2: "Avensis"
}
}
AwesomePluginMagic(obj);
Which should give me
<select name="primary">
<option value="0" selected>Ford</option>
<option value="1">Toyota</option>
</select>
<select name="secondary">
<option value="1">Focus</option>
<option value="2">Fiesta</option>
<option class="hidden" value="1">Prius</option>
<option class="hidden" value="2">Avensis</option>
</select>
And should then listen for changes and visualize the appropriate options in the relevant select element.
Disclaimer: I don't want you to write a function for me, I'm asking for a plugin already made, but if you insist I'll use it. Also the input object can be of any format, I just made up one for the example.
An example on how to implement this without any library.
(function() {
'use strict';
var obj = {
Ford: {
1: "Focus",
2: "Fiesta"
},
Toyota: {
1: "Prius",
2: "Avensis"
}
};
var manufacturers = Object.keys(obj);
var selectedManufacturer = manufacturers[0];
var models = hashTokeyValuePairArray(obj[selectedManufacturer]);
var selectedModel = models[0];
var selectedCar = { make: selectedManufacturer, model: selectedModel.value };
bindManufacturer();
bindModel();
bindSelectedCar();
function hashTokeyValuePairArray(o) {
var k = Object.keys(o);
return k.map(function(k) { return { key: k, value: o[k] }; });
}
function bindSelectedMake() {
var el = document.getElementById("selectedMake");
el.innerText = selectedManufacturer;
}
function bindSelectedModel() {
var el = document.getElementById("selectedModel");
el.innerText = selectedModel.value;
}
function bindSelectedCar() {
bindSelectedMake();
bindSelectedModel();
selectedCar = { make: selectedManufacturer, model: selectedModel.value };
console.log(selectedCar);
}
function bindManufacturer() {
var el = document.getElementById("make");
var opts = manufacturers.map(function(m) { return "<option>" + m + "</option>"; });
el.innerHTML = opts.join('');
el.options.selectedIndex = 0;
el.addEventListener('change', function() {
selectedManufacturer = manufacturers[this.options.selectedIndex];
models = hashTokeyValuePairArray(obj[selectedManufacturer]);
selectedModel = models[0];
bindModel(true);
bindSelectedCar();
});
}
function bindModel(skipEventBinding) {
var el = document.getElementById("model");
var opts = models.map(function(m) { return "<option value=\"" + m.key + "\">" + m.value + "</option>"; });
el.innerHTML = opts.join('');
el.options.selectedIndex = 0;
if (!skipEventBinding) {
el.addEventListener('change', function() {
selectedModel = models[this.options.selectedIndex];
bindSelectedCar();
});
}
}
}());
#make, #model { width: 100px; margin-bottom:5px }
<label for="make">Make:</label> <select id="make"></select>
<label for="model">Model:</label> <select id="model">
</select>
<div>
Selected Car: <span id="selectedMake"></span> <span id="selectedModel"></span>
</div>
Added a data-car
value to the secondary options so they have a reference to the primary select
. Also added a change
listener for the primary which changes the .hidden
for the secondary options based on the selected item.
Disclaimer: It will work with the current format, if obj has a different format it needs to be modified. I don't think it's possible though, unless some restrictions are set for the format of the given object.
function AwesomePluginMagic(obj) {
var prm = document.createElement('select');
var scnd = document.createElement('select');
prm.name = "primary";
scnd.name = "secondary";
var i = 1;
for(var itm in obj) {
var popt = document.createElement('option');
popt.value = i;
popt.innerHTML = itm;
prm.appendChild(popt);
var j = 1;
for(var sub in obj[itm]) {
var sopt = document.createElement('option');
sopt.setAttribute('data-car', i);
sopt.value = j;
sopt.innerHTML = obj[itm][sub];
if(i > 1)
sopt.className = "hidden";
scnd.appendChild(sopt);
j++;
}
i++;
}
var dv = document.getElementById('dv');
dv.appendChild(prm);
dv.appendChild(scnd);
prm.addEventListener('change', function() {
var selected = false;
var o = scnd.options;
for(var i = 0; i < o.length; i++) {
if(o[i].getAttribute('data-car') == this.value) {
o[i].className = "";
if(!selected) {
o[i].setAttribute('selected', 'selected');
selected = true;
}
}
else
o[i].className = "hidden";
}
});
}
jsfiddle DEMO
JS is object oriented, why not use objects, something like this:
var // Data
cars = {
Ford: {
1: "Focus",
2: "Fiesta",
},
Toyota: {
1: "Prius",
2: "Avensis"
}
},
// The AwesomePlugin
selectPair = Object.create({}, {
update: {value: function (select, obj) {
var keys = Object.keys(obj).sort(function (a, b) {return a.localeCompare(b);}),
n, eN, option, element = this[select] || document.createElement('select');
element.length = 0;
for (n = 0, eN = keys.length; n < eN; n ++) {
option = document.createElement('option');
option.text = (typeof obj[keys[n]] === 'object') ? keys[n] : obj[keys[n]];
option.value = (typeof obj[keys[n]] === 'object') ? keys[n] : obj[keys[n]];
element.appendChild(option);
}
return element;
}},
init: {value: function (obj, target) {
var that = this, keys = Object.keys(obj).sort(function (a, b) {return a.localeCompare(b);});
this.host = target.appendChild(this.update('host', obj));
this.child = target.appendChild(this.update('child', obj[keys[0]]));
this.host.addEventListener('change', function () {
that.update('child', obj[that.host.value]);
});
return this;
}}
});
// Usage
Object.create(selectPair).init(cars, document.body);
The prototype is reusable, you can create multiple "select pairs" just by creating a new object using selectPair
prototype and passing the needed data object and the element to append the select
elements.