你是对的。有很多类似的问题,但它们之间似乎总是没有什么区别。
我以前写过这样的小部件。这是一种与gibberish 的answer 完全不同的方法。
它涉及一个您可以像这样使用的小部件:
var widget = new DropdownWidget(options);
widget.onComplete(function(vals) {
console.log(vals);
});
widget.addTo(document.body);
提供给onComplete 的处理程序将接收对象,例如
{
"main_list": "mobile list",
"brands": "Samsung",
"samsung_select": "4.2"
}
您根本不会使用任何标记。相反,它将配置一个像这样的对象:
var options = [
{
id: 1,
name: "main_list",
defaultVal: "Select Your List",
choices: [
{value: "mobile", text: "mobile list", nextId: 2},
{value: "laptop", text: "laptop list", nextId: 3}
]
},
{
id: 2,
name: "brands",
defaultVal: "Select Your Mobile Brand",
choices: [
{value: "mobile_1", text: "Samsung", nextId: 4},
{value: "mobile_2", text: "Nokia", nextId: 5}
]
},
{
id: 3,
name: "brands",
defaultVal: "Select Your Laptop Brand",
choices: [
{value: "laptop_1", text: "HP"},
{value: "laptop_2", text: "Dell"}
]
},
{
id: 4,
name: "samsung_select",
defaultVal: "Select Your Andriod Version",
choices: [
{value: "android_1", text: "4.1"},
{value: "android_2", text: "4.2"}
]
},
{
id: 5,
name: "nokia_select",
defaultVal: "Select Your Windows Version",
choices: [
{value: "windows_1", text: "windows 8"},
{value: "windows_2", text: "windows 8.1"}
]
}
];
请注意,如果您不使用重复的名称,这可能会有所不同。 ids 可以被删除,nextId 可以变成nextName。
这是这样一个小部件的幼稚实现:
var DropdownWidget = (function() {
var DropdownWidget = function(options) {
// TODO: type-checking on options: should be array of acceptable configurations...
this.options = options;
this.selectedVals = [];
this.showing = false;
this.handlers = [];
};
DropdownWidget.prototype.onComplete = function(handler) {
this.handlers.push(handler);
};
DropdownWidget.prototype.addTo = function(element) {
if (this.showing) {
alert("Oops!"); // TODO: real error handling, or should this be moveable?
return;
}
var dropdown = createDropdown(this.options[0]);
this.elements = [dropdown];
addHandlers(this, dropdown, options, 0);
element.appendChild(dropdown);
};
var createDropdown = function(config) {
var select = document.createElement("SELECT");
select.name = config.name;
select.options[select.options.length] = new Option(config.defaultVal, "default");
config.choices.forEach(function(choice) {
select.options[select.options.length] = new Option(choice.text, choice.value);
});
return select;
};
var addHandlers = function(widget, select, options, index) {
select.onchange = function() {
removeSubsequentSelects(widget, options, index);
if (this.selectedIndex > 0) {
var choice = options[index].choices[this.selectedIndex - 1];
if (widget.selectedVals[widget.selectedVals.length - 1] !== options[index]) {
widget.selectedVals.push(options[index]);
}
if (choice.nextId) {
var nextIndex = findIndex(function(item) {
return item.id === choice.nextId;
}, options);
if (nextIndex > -1) {
var dropdown = createDropdown(options[nextIndex]);
widget.elements.push(dropdown);
addHandlers(widget, dropdown, options, nextIndex);
this.parentNode.appendChild(dropdown);
}
} else {
complete(widget);
}
}
}
};
var removeSubsequentSelects = function(widget, options, index) {
var start = findIndex(function(selected) {
return selected == options[index];
}, widget.selectedVals);
var idx = start;
if (idx > -1) {
while (++idx < widget.elements.length) {
widget.elements[idx].parentNode.removeChild(widget.elements[idx]);
}
widget.elements.length = widget.selectedVals.length = start + 1;
}
}
var findIndex = function(predicate, list) {
var idx = -1;
while (++idx < list.length) {if (predicate(list[idx])) {return idx;}}
return -1;
};
var complete = function(widget) {
var vals = widget.selectedVals.map(function(val, idx) {
return {name: val.name, val: val.choices[widget.elements[idx].selectedIndex - 1].text}
}).reduce(function(memo, obj) {memo[obj.name] = obj.val; return memo;}, {});
widget.handlers.forEach(function(handler) {
handler(vals);
});
};
return DropdownWidget;
}());
您可以在 JSFiddle 上看到它的实际效果。
有很多事情可以做得更好。我看到的最大问题是构造的 SELECTS 的 DOM 位置非常简单。
无论如何,这是一种不同的方法。