【发布时间】:2011-09-20 17:09:18
【问题描述】:
是否可以在表单下拉列表中创建嵌套选项字段,就像创建嵌套 ul 列表一样?
既然改变只是美学上的,那么用css可以做到这一点吗?
【问题讨论】:
是否可以在表单下拉列表中创建嵌套选项字段,就像创建嵌套 ul 列表一样?
既然改变只是美学上的,那么用css可以做到这一点吗?
【问题讨论】:
您可以使用<optgroup> 创建单层嵌套...
<select>
<optgroup label="Options 1">
<option>Option 1.1</option>
<option>Option 1.2</option>
</optgroup>
<optgroup label="Options 2">
<option>Option 2.1</option>
<option>Option 2.2</option>
</optgroup>
</select>
示例:http://jsfiddle.net/JaZAm/1/
请注意,组标签不是可选选项。在这种情况下,我建议使用在他的评论中与主页链接的问题的最佳答案中提到的文本缩进解决方案。
【讨论】:
您不能嵌套多个<option>s。如果要对 <option> 元素进行分组,请使用 <optgroup>。
【讨论】:
不,不是真的。有一个 optgroup 标签是不可选择的标题,您可以在部分之间添加,但 <select> 元素不能嵌套。
【讨论】:
考虑使用optgroup 标签。至于样式支持,有一些支持,但您可以由浏览器支配,因为它是一个表单元素。
http://www.456bereastreet.com/lab/styling-form-controls-revisited/select-single-optgroup/
如果您需要大量重新设置样式,请考虑使用嵌套的 UL 结构构建您自己的 UI 小部件,并通过 JavaScript 为其提供交互。
【讨论】:
可以嵌套选项,甚至可以选择它们。但是您需要使用 JavaScript。在这个例子中,代码是用 TypeScript (Angular v6) 编写的,但是你可以用任何其他现代 Javascript 框架,纯 Javascript 或 jQuery 来做同样的事情。
假设 A、B 和 C 是您的选择:
let options = [
'A',
'B',
'C'
];
您希望将它们显示为:A->B->C(A 是 B 的父级,B 是 C 的父级)。
您希望用户能够选择 A 和 C,但不能选择 B。让我们创建一个简单的界面,让这更容易:
interface CoolOption {
content: string,
selectable: boolean,
depth: number
};
现在,您的选项将如下所示:
let selectedOption: string = null;
let options: CoolOption[] = new Array<CoolOption>();
let A: CoolOption = {
content: 'A',
selectable: true,
depth: 0
};
let B: CoolOption = {
content: 'B',
selectable: false,
depth: 1
};
let C: CoolOption = {
content: 'A',
selectable: true,
depth: 2
};
您选择的模板将如下所示:
<mat-select>
<mat-option *ngFor="let option of options" (change)="setSelectedOption($event)">
<span [style.padding-left.px]="option.depth * 5">
{{
option.content
}}
</span>
</mat-option>
</mat-select>
简单解释:当用户选择一个选项时,setSelectedOption 函数(我们将在后面定义)将被调用。 另外,CSS padding-left 属性会受到我们之前设置的 'depth' 属性的影响。
这样我们将“模拟”嵌套效果。
最后,我们的 setSelectedOption 函数:
setSelectedOption(option: CoolOption) {
if (option.selectable) {
this.selectedOption = option.content;
}
}
基本上如果它是可选择的,selectedOption 的值就会改变。否则它将保持原样。
希望这对某人有所帮助,很抱歉没有足够的时间在纯 JavaScript 中复制示例。
【讨论】:
我采用这种方法是因为我找不到我正在搜索的内容。嵌套的手风琴选择。它的 CSS 非常简单,可以改进。您唯一需要的是一个带有要添加到选择中的键和值的对象。 Keys 将是子组,而键 values(数组和单个元素)将是可选项目。
一旦你有了你的数组,你唯一需要做的就是调用
initAccordeon(obj);
将你的数据对象作为参数,嵌套的手风琴就会出现:
const obj = {
Cars: {
SwedishCars: [
"Volvo",
"Saab"
],
GermanCars: [
"Mercedes",
{
Audi: [
"Audi A3",
"Audi A4",
"Audi A5"
]
}
]
},
Food: {
Fruits: [
"Orange",
"Apple",
"Banana"
],
SaltyFoods: [
"Pretzels",
"Burger",
"Noodles"
],
Drinks: "Water"
}
};
initAccordeon(obj); // <--------------------------- Call initialization
function accordeonAddEvents() {
Array.from(document.getElementsByClassName("accordeon-header")).forEach(function(header) {
if (header.getAttribute("listener") !== "true") {
header.addEventListener("click", function() {
this.parentNode.getElementsByClassName("accordeon-body")[0].classList.toggle("hide");
});
header.setAttribute("listener", "true");
}
});
Array.from(document.getElementsByClassName("button-group")).forEach(function(but) {
if (but.getAttribute("listener") !== "true") {
but.addEventListener("click", function() {
if (this.getAttribute("depth") === "-1") {
let header = this;
while ((header = header.parentElement) && header.className !== "accordeon");
header.getElementsByClassName("accordeon-header")[0].innerHTML = this.innerHTML;
return;
}
const groups = Array.from(this.parentNode.getElementsByClassName("accordeon-group"));
groups.forEach(g => {
if (g.getAttribute("uuid") === this.getAttribute("uuid") &&
g.getAttribute("depth") === this.getAttribute("depth")) {
g.classList.toggle("hide");
}
});
});
but.setAttribute("listener", "true");
}
});
}
function initAccordeon(data) {
accordeons = Array.from(document.getElementsByClassName("accordeon-body"));
accordeons.forEach(acc => {
acc.innerHTML = "";
const route = (subObj, keyIndex = 0, parent = acc, depth = 0) => {
const keys = Object.keys(subObj);
if (typeof subObj === 'object' && !Array.isArray(subObj) && keys.length > 0) {
while (keyIndex < keys.length) {
var but = document.createElement("button");
but.className = "button-group";
but.setAttribute("uuid", keyIndex);
but.setAttribute("depth", depth);
but.innerHTML = keys[keyIndex];
var group = document.createElement("div");
group.className = "accordeon-group hide";
group.setAttribute("uuid", keyIndex);
group.setAttribute("depth", depth);
route(subObj[keys[keyIndex]], 0, group, depth + 1);
keyIndex++;
parent.append(but);
parent.append(group);
}
} else {
if (!Array.isArray(subObj)) subObj = [subObj];
subObj.forEach((e, i) => {
if (typeof e === 'object') {
route(e, 0, parent, depth);
} else {
var but = document.createElement("button");
but.className = "button-group";
but.setAttribute("uuid", i);
but.setAttribute("depth", "-1");
but.innerHTML = e;
parent.append(but);
}
});
}
};
route(data);
});
accordeonAddEvents();
}
.accordeon {
width: 460px;
height: auto;
min-height: 340px;
font-size: 20px;
cursor: pointer;
user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
display: block;
position: relative;
z-index: 10;
}
.accordeon-header {
display: inline-block;
width: 450px;
border: solid 0.1vw black;
border-radius: 0.2vw;
background-color: white;
padding-left: 10px;
color: black;
}
.accordeon-header:hover {
opacity: 0.7;
}
.accordeon-body {
display: block;
position: absolute;
}
.button-group {
display: block;
cursor: pointer;
width: 460px;
text-align: left;
font-size: 20px;
font-weight: bold;
}
.accordeon-group {
padding-left: 20px;
}
.accordeon-group .button-group {
width: 100%;
}
.button-group[depth="-1"] {
color: green;
}
.hide {
display: none;
}
<div class="accordeon">
<span class="accordeon-header">Select something</span>
<div class="accordeon-body hide">
</div>
</div>
【讨论】: