【发布时间】:2008-11-05 08:57:09
【问题描述】:
我想创建一个带有下拉列表的文本字段,让用户可以选择一些预定义的值。用户还应该能够键入新值或从下拉列表中选择预定义的值。我知道我可以为此使用两个小部件,但在我的应用中,如果将它统一在一个小部件中会更符合人体工程学。
是否有标准小部件或我必须使用第三方 javascript?
浏览器的可移植性如何?
【问题讨论】:
标签: html combobox html-select
我想创建一个带有下拉列表的文本字段,让用户可以选择一些预定义的值。用户还应该能够键入新值或从下拉列表中选择预定义的值。我知道我可以为此使用两个小部件,但在我的应用中,如果将它统一在一个小部件中会更符合人体工程学。
是否有标准小部件或我必须使用第三方 javascript?
浏览器的可移植性如何?
【问题讨论】:
标签: html combobox html-select
您可以通过在 HTML5 中使用 <datalist> 标记来完成此操作。
<input type="text" name="product" list="productName"/>
<datalist id="productName">
<option value="Pen">Pen</option>
<option value="Pencil">Pencil</option>
<option value="Paper">Paper</option>
</datalist>
如果您在浏览器中双击输入文本,则会出现一个包含已定义选项的列表。
【讨论】:
这可以在纯 HTML、CSS 和 JQuery 的帮助下实现。我创建了一个示例页面:
$(document).ready(function(){
$(".editableBox").change(function(){
$(".timeTextBox").val($(".editableBox option:selected").html());
});
});
.editableBox {
width: 75px;
height: 30px;
}
.timeTextBox {
width: 54px;
margin-left: -78px;
height: 25px;
border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<select class="editableBox">
<option value="1">01:00</option>
<option value="2">02:00</option>
<option value="3">03:00</option>
<option value="4">04:00</option>
<option value="5">05:00</option>
<option value="6">06:00</option>
<option value="7">07:00</option>
<option value="8">08:00</option>
<option value="9">09:00</option>
<option value="10">10:00</option>
<option value="11">11:00</option>
<option value="12">12:00</option>
<option value="13">13:00</option>
<option value="14">14:00</option>
<option value="15">15:00</option>
<option value="16">16:00</option>
<option value="17">17:00</option>
<option value="18">18:00</option>
<option value="19">19:00</option>
<option value="20">20:00</option>
<option value="21">21:00</option>
<option value="22">22:00</option>
<option value="23">23:00</option>
<option value="24">24:00</option>
</select>
<input class="timeTextBox" name="timebox" maxlength="5"/>
</div>
【讨论】:
基于 CSS 和一行 JavaScript 代码的非常简单的实现(仅基本功能)。
.dropdown {
position: relative;
width: 200px;
}
.dropdown select {
width: 100%;
}
.dropdown > * {
box-sizing: border-box;
height: 1.5em;
}
.dropdown input {
position: absolute;
width: calc(100% - 20px);
}
<div class="dropdown">
<input type="text" />
<select onchange="this.previousElementSibling.value=this.value; this.previousElementSibling.focus()">
<option>This is option 1</option>
<option>Option 2</option>
</select>
</div>
请注意:它使用previousElementSibling(),即not supported in older browsers(低于IE9)
【讨论】:
this.previousElementSibling而不是document.getElementById()??这将使它得到一些更旧的浏览器的支持
<select> 标签只允许使用预定义的条目。您的问题的典型解决方案是使用一个标记为“其他”的条目和一个禁用的编辑字段 (<input type="text")。添加一些 JavaScript 以仅在选择“其他”时启用编辑字段。
也许可以以某种方式创建一个允许直接编辑的下拉菜单,但 IMO 不值得付出努力。如果是这样,亚马逊、谷歌或微软就会这样做;-) 只需用最简单的解决方案完成工作。它更快(您的老板可能喜欢这样)并且通常更易于维护(您可能喜欢这样)。
【讨论】:
带有文本框的组合框(用于预定义值和用户定义值。)
【讨论】:
我不确定有没有办法在没有 javascript 的情况下自动完成。
您需要的是在浏览器端运行的东西,以便在用户做出选择时将您的表单提交回服务器 - 因此是 javascript。
此外,请确保您为那些关闭了 javascript 的用户提供了替代方法(即提交按钮)。
一个很好的例子:Combo-Box Viewer
我昨天有一个更复杂的组合框,用这个dhtmlxCombo ,使用 ajax 在大量数据中检索相关值。
【讨论】:
一点 CSS 就完成了 fiddle
<div style="position: absolute;top: 32px; left: 430px;" id="outerFilterDiv">
<input name="filterTextField" type="text" id="filterTextField" tabindex="2" style="width: 140px;
position: absolute; top: 1px; left: 1px; z-index: 2;border:none;" />
<div style="position: absolute;" id="filterDropdownDiv">
<select name="filterDropDown" id="filterDropDown" tabindex="1000"
onchange="DropDownTextToBox(this,'filterTextField');" style="position: absolute;
top: 0px; left: 0px; z-index: 1; width: 165px;">
<option value="-1" selected="selected" disabled="disabled">-- Select Column Name --</option>
</select>
【讨论】:
HTML 没有内置的可编辑下拉列表或组合框,但我在 an article 中实现了一个主要是 CSS 的解决方案。
您可以看到完整的演示 here,但总而言之,编写 HTML 如下:
<span class="combobox withtextlist">
<input value="Fruit">
<span tabindex="-1" class="downarrow"></span>
<select size="10" class="sticky">
<option>Apple</option>
<option>Banana</option>
<option>Cherry</option>
<option>Dewberry</option>
</select>
</span>
并使用这样的 CSS 来设置它的样式(这是为两个组合框设计的,组合框有一个向下箭头 ▾ 按钮,以及单击时打开的下拉菜单,并且可能有不同的样式):
/* ------------------------------------------ */
/* ----- combobox / dropdown list styling */
/* ------------------------------------------ */
.combobox {
/* Border slightly darker than Chrome's <select>, slightly lighter than FireFox's */
border: 1px solid #999;
padding-right: 1.25em; /* leave room for ▾ */
}
.dropdown, .combobox {
/* "relative" and "inline-block" (or just "block") are needed
here so that "absolute" works correctly in children */
position: relative;
display: inline-block;
}
.combobox > .downarrow, .dropdown > .downarrow {
/* ▾ Outside normal flow, relative to container */
display: inline-block;
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 1.25em;
cursor: default;
nav-index: -1; /* nonfunctional in most browsers */
border-width: 0px; /* disable by default */
border-style: inherit; /* copy parent border */
border-color: inherit; /* copy parent border */
}
/* Add a divider before the ▾ down arrow in non-dropdown comboboxes */
.combobox:not(.dropdown) > .downarrow {
border-left-width: 1px;
}
/* Auto-down-arrow if one is not provided */
.downarrow:empty::before {
content: '▾';
}
.downarrow::before, .downarrow > *:only-child {
text-align: center;
/* vertical centering trick */
position: relative;
top: 50%;
display: block; /* transform requires block/inline-block */
transform: translateY(-50%);
}
.combobox > input {
border: 0
}
.dropdown > *:last-child,
.combobox > *:last-child {
/* Using `display:block` here has two desirable effects:
(1) Accessibility: it lets input widgets in the dropdown to
be selected with the tab key when the dropdown is closed.
(2) It lets the opacity transition work.
But it also makes the contents visible, which is undesirable
before the list drops down. To compensate, use `opacity: 0`
and disable mouse pointer events. Another side effect is that
the user can select and copy the contents of the hidden list,
but don't worry, the selected content is invisible. */
display: block;
opacity: 0;
pointer-events: none;
transition: 0.4s; /* fade out */
position: absolute;
left: 0;
top: 100%;
border: 1px solid #888;
background-color: #fff;
box-shadow: 1px 2px 4px 1px #666;
box-shadow: 1px 2px 4px 1px #4448;
z-index: 9999;
min-width: 100%;
box-sizing: border-box;
}
/* List of situations in which to show the dropdown list.
- Focus dropdown or non-last child of it => show last-child
- Focus .downarrow of combobox => show last-child
- Stay open for focus in last child, unless .less-sticky
- .sticky last child stays open on hover
- .less-sticky stays open on hover, ignores focus in last-child */
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > .downarrow:focus ~ *:last-child,
.combobox > .sticky:last-child:hover,
.dropdown > .sticky:last-child:hover,
.combobox > .less-sticky:last-child:hover,
.dropdown > .less-sticky:last-child:hover,
.combobox > *:last-child:focus:not(.less-sticky),
.dropdown > *:last-child:focus:not(.less-sticky) {
display: block;
opacity: 1;
transition: 0.15s;
pointer-events: auto;
}
/* focus-within not supported by Edge/IE. Unsupported selectors cause
the entire block to be ignored, so we must repeat all styles for
focus-within separately. */
.combobox > *:last-child:focus-within:not(.less-sticky),
.dropdown > *:last-child:focus-within:not(.less-sticky) {
display: block;
opacity: 1;
transition: 0.15s;
pointer-events: auto;
}
/* detect Edge/IE and behave if though less-sticky is on for all
dropdowns (otherwise links won't be clickable) */
@supports (-ms-ime-align:auto) {
.dropdown > *:last-child:hover {
display: block;
opacity: 1;
pointer-events: auto;
}
}
/* detect IE and do the same thing. */
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.dropdown > *:last-child:hover {
display: block;
opacity: 1;
pointer-events: auto;
}
}
.dropdown:not(.sticky) > *:not(:last-child):focus,
.downarrow:focus, .dropdown:focus {
pointer-events: none; /* Causes second click to close */
}
.downarrow:focus {
outline: 2px solid #8BF; /* Edge/IE can't do outline transparency */
outline: 2px solid #48F8;
}
/* ---------------------------------------------- */
/* Optional extra styling for combobox / dropdown */
/* ---------------------------------------------- */
*, *:before, *:after {
/* See https://css-tricks.com/international-box-sizing-awareness-day/ */
box-sizing: border-box;
}
.combobox > *:first-child {
display: inline-block;
width: 100%;
box-sizing: border-box; /* so 100% includes border & padding */
}
/* `.combobox:focus-within { outline:...}` doesn't work properly
in Firefox because the focus box is expanded to include the
(possibly hidden) drop list. As a workaround, put focus box on
the focused child. It is barely-visible so that it doesn't look
TOO ugly if the child isn't the same size as the parent. It
may be uglier if the first child is not styled as width:100% */
.combobox > *:not(:last-child):focus {
outline: 2px solid #48F8;
}
.combobox {
margin: 5px;
}
您还需要一些 JavaScript 来将列表与文本框同步:
function parentComboBox(el) {
for (el = el.parentNode; el != null && Array.prototype.indexOf.call(el.classList, "combobox") <= -1;)
el = el.parentNode;
return el;
}
// Uses jQuery
$(".combobox.withtextlist > select").change(function() {
var textbox = parentComboBox(this).firstElementChild;
textbox.value = this[this.selectedIndex].text;
});
$(".combobox.withtextlist > select").keypress(function(e) {
if (e.keyCode == 13) // Enter pressed
parentComboBox(this).firstElementChild.focus(); // Closes the popup
});
【讨论】:
没有 CSS 的简单 HTML + Javascript 方法
function editDropBox() {
var cSelect = document.getElementById('changingList');
var optionsSavehouse = [];
if(cSelect != null) {
var optionsArray = Array.from(cSelect.options);
var arrayLength = optionsArray.length;
for (var o = 0; o < arrayLength; o++) {
var option = optionsArray[o];
var oVal = option.value;
if(oVal > 0) {
var localParams = [];
localParams.push(option.text);
localParams.push(option.value);
//localParams.push(option.selected); // if needed
optionsSavehouse.push(localParams);
}
}
}
var hidden = ("<input id='hidden_select_options' type='hidden' value='" + JSON.stringify(optionsSavehouse) + "' />");
var cSpan = document.getElementById('changingSpan');
if(cSpan != null) {
cSpan.innerHTML = (hidden + "<input size='2' type='text' id='tempInput' name='fname' onchange='restoreDropBox()'>");
}
}
function restoreDropBox() {
var cSpan = document.getElementById('changingSpan');
var cInput = document.getElementById('tempInput');
var hOptions = document.getElementById('hidden_select_options');
if(cSpan != null) {
var optionsArray = [];
if(hOptions != null) {
optionsArray = JSON.parse(hOptions.value);
}
var selectList = "<select id='changingList'>\n";
var arrayLength = optionsArray.length;
for (var o = 0; o < arrayLength; o++) {
var option = optionsArray[o];
selectList += ("<option value='" + option[1] + "'>" + option[0] + "</option>\n");
}
if(cInput != null) {
selectList += ("<option value='-1' selected>" + cInput.value + "</option>\n");
}
selectList += ("<option value='-2' onclick='editDropBox()'>- Edit -</option>\n</select>");
cSpan.innerHTML = selectList;
}
}
<span id="changingSpan">
<select id="changingList">
<option value="1">Apple</option>
<option value="2">Banana</option>
<option value="3">Cherry</option>
<option value="4">Dewberry</option>
<option onclick="editDropBox()" value="-2">- Edit -</option>
</select>
</span>
【讨论】: