如何在HTML中创建可编辑的下拉列表?

70
我想创建一个文本框,其中带有下拉列表,让用户选择一些预定义的值。用户还应该能够键入新值或从下拉列表中选择预定义的值。我知道我可以使用两个小部件来实现,但在我的应用程序中,如果能统一在一个小部件中会更加方便。
是否有标准的小部件可用,还是必须使用第三方JavaScript?
浏览器兼容性如何?
12个回答

152
你可以通过在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>

如果您在浏览器中双击输入文本,则会显示带有定义选项的列表。


12
<datalist> 标签目前并不是所有浏览器都支持 - James Newton
5
我认为这仍然是正确的方法。你可以使用某个插件(https://miketaylr.com/code/datalist.html),这样你的实现就可以跨浏览器并具备未来性 :) - kbtz
2
@AaronHudon 不是这样。数据列表是一个建议列表,而不是选择替代品。为了更好地理解我的意思,您应该尝试创建一个带有0-9选项的数据列表。由于我之前的声明,这些选项将不会显示。 - David
2
一个潜在的缺点是,如果输入有值,则仅显示与输入当前文本匹配的选项,因此用户看不到可用选项。 - Phil Haselden
2
它会被支持吗?这个答案来自2013年;5年后的2018年,只有两个浏览器(Android的Chrome和三星Internet)完全支持它([来源](https://caniuse.com/#feat=datalist))。 - Anomaly
显示剩余3条评论

26

使用纯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>


7
请注意,对于任何扫描此内容的人,jQuery仅被用来将下拉选择复制到文本框中。 "秘密配方"(在此页面上的其他位置实现)只是20像素间隙,让您可以看到/单击下拉箭头。 - i336_

18

基于 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(),在旧浏览器中(低于IE9)不受支持

2
您应该在回答中包含代码,这样我们就不必去其他可能会消失的网站了。 - titanofold
链接腐败是一种癌症! - jpaugh
为什么要使用 this.previousElementSibling 而不是 document.getElementById()?这样可以支持一些更旧的浏览器。 - WuerfelDev
谢谢,与我找到的其他所有解决方案相比,这是一种优雅的解决方案 - 而且它可以轻松地在我的页面中使用! - Jon8RFC
1
@titanofold。我已经编辑了帖子,现在你可以在这里查看代码片段。 - bilalmohib
document.getElementById() 在 Web 组件和其他不适合全局访问的情况下使用起来很困难。谢谢,我真的很喜欢这个解决方案。 - Jens Lincke

16

最好的方法可能是使用第三方库。

jQuery UIjQuery UIdojodojo中都实现了你要找的内容。jQuery更受欢迎,但是dojo允许您在HTML中声明式地定义小部件,这更符合您的需求。

使用哪个取决于您的风格,但两者都为跨浏览器工作开发,并且两者将比复制粘贴的代码更新得更频繁。


9
<select>标签只允许使用预定义的条目。解决您的问题的典型方法是添加一个标记为“其他”的条目和一个禁用的编辑字段(<input type="text")。添加一些JavaScript代码,使只有在选择“其他”时才能启用编辑字段。
可能有一种方法可以创建下拉列表,允许直接编辑,但在我看来,这不值得努力。如果值得,亚马逊、谷歌或微软会做到的;-)只需使用最简单的解决方案完成工作。它更快(您的老板可能会喜欢这个),通常也更容易维护(您可能会喜欢这个)。

Google Reader现在实际上已经有了,在搜索框附近的下拉菜单中。 - André Paramés

4

3
HTML没有内置的可编辑下拉列表或组合框,但我在一篇文章中实现了一个主要基于CSS的解决方案。
你可以在这里看到完整的演示,但简要来说,像这样编写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来将列表与文本框同步:

您还需要一些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
});

1

我不确定有没有一种自动完成的方法,而不使用 JavaScript。

您需要的是在浏览器端运行的东西,在用户进行选择时将表单提交回服务器 - 因此需要使用 JavaScript。

另外,请确保为那些关闭了 JavaScript 的用户提供备用手段(例如提交按钮)。

一个很好的例子: Combo-Box Viewer

昨天我甚至使用了这个 dhtmlxCombo 更复杂的组合框,使用 ajax 从大量数据中检索相关值。


1

组合框(combobox)很遗憾地被忽略在HTML规范中。

唯一的管理方式,非常不幸的是,就是自己编写或使用预先构建的组合框。这个看起来相当简单。我在一个开源应用程序中使用这个,虽然不幸的是商业使用需要付费。


1
一点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>


有趣的黑客技巧 :) 但是你的小提琴实际上不允许从列表中选择某些内容。这绝对需要一些JavaScript才能运行。 - C-F
4
“DropDownTextToBox”函数必须被定义。但是在你的fiddle中没有定义它。 - C-F

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接