css实现列表下拉菜单_逐行:点击打开下拉列表和菜单的高级CSS技巧

css实现列表下拉菜单

by David Piepgrass

由David Piepgrass

逐行:点击打开下拉列表和菜单的高级CSS技巧 (Line-by-line: advanced CSS tricks for click-to-open drop-down lists and menus)

For as long as I can remember, there were always two kinds of selectors.

据我所知,选择器始终有两种。

There was the kind where the text on top could be edited, and the kind where it couldn’t. HTML includes the second kind, no problem:

有一种可以编辑顶部的文本,而没有一种可以编辑。 HTML包含第二种,没问题:

<select>
    <option>Apple</option>  
    <option>Banana</option>  
    <option>Cherry</option>  
    <option>Dewberry</option>
</select>

But I was shocked to learn that the first kind doesn’t exist in HTML. Oh, there’s a thing called a datalist, but it doesn’t work right — users can’t click something to view the entire list, and as you begin typing, items immediately start disappearing if they don’t start with the same string that the user typed.

但是我很震惊地得知HTML中不存在第一类。 哦,有一个叫做datalist的东西,但是它不能正常工作-用户无法单击某些东西来查看整个列表,并且在您开始键入内容时,如果项目的开头不是相同的字符串,则它们会立即消失。用户输入。

But CSS is a styling tool of impressive power: entire video games have been built out of CSS, HTML and a few picture files. (Great, I just lost half my audience.)

但是CSS是强大的样式工具: 整个 视频 游戏都是基于CSS,HTML和一些图片文件构建的。 (太好了,我失去了一半的观众。)

This doesn’t mean CSS can do anything, but it does mean there are at least “hacks” to accomplish a wide variety of tricks. Those of you who are done playing games are probably interested to learn about some of the tricks of the trade, and I think there’s a lot to learn by figuring out how to do a combo box.

这并不意味着CSS可以做任何事情 ,而是意味着至少有“ hacks”来完成各种各样的技巧。 那些玩过游戏的人可能有兴趣了解一些交易技巧,我认为通过弄清楚组合框的方法可以学到很多东西。

In this article you’ll learn how this thing works:

在本文中,您将学习此事物的工作原理:

In Windows we call them “combo boxes”, since they combine a top part (usually a text field) with a popup part (usually a drop-down list).

在Windows中,我们称它们为“组合框”,因为它们将顶部(通常是文本字段)与弹出部分(通常是下拉列表)结合在一起。

如何使用它 (How to use it)

The combo box can be constructed out of divs and/or spans. Just remember that an HTML parser has a few nesting rules. For instance, it does not allow p to be an ancestor of div or ul, and span cannot be an ancestor of p or div. (These rules do not apply to JavaScript/React code that edits the DOM)

组合框可以由div和/或span组成。 请记住,HTML解析器具有一些嵌套规则。 例如,它不允许pdivul的祖先,而span不能是pdiv的祖先。 (这些规则不适用于编辑DOMJavaScript / React代码)

CSS will expect three children: first the top part (content to always show), then <span class="downarrow" tabindex="-1"></span> for the down arrow, and finally the content to show inside the drop-down box:

CSS将包含三个子元素:首先是顶部(始终显示内容),然后是<span class= "downarrow" tabindex= "-1" ></span>作为向下箭头,最后是内容显示在下拉菜单中下框:

<div class="combobox">
  <div>Simple combo box</div>
  <div tabindex="-1" class="downarrow"></div>
  <div>
    Contents of drop-down popup go here
  </div>
</div>

By default, the drop-down will only open when the down arrow (▾) is clicked. To make the box open when the top content is clicked, you need to add the dropdown class to the combobox, and add a tabindex="0" attribute to the first child:

默认情况下,仅当单击向下箭头(▾)时下拉列表才会打开。 要在单击顶部内容时使框打开,需要将dropdown添加到combobox ,并向第一个孩子添加tabindex="0"属性:

<div class="combobox dropdown">
  <div tabindex="0">Simple combo box</div>
  <div tabindex="-1" class="downarrow"></div>
  <div>
    Contents of drop-down popup go here
  </div>
</div>

Note:tabindex="-1" means “you can click to give it the focus, but you can’t focus it using Tab on the keyboard”. tabindex="0" means “you can focus it with a click or with the tab key, and the browser will choose the order in which different elements are focused by the Tab key.” Unlike a <select> element, the popup box won’t be able to go outside the browser window (this may be an intentional limitation of all user-defined content — if user-defined content could extend beyond the edge of the page area, there might be a security risk of web sites trying to confuse or trick users.)

注意: tabindex="-1"意思是“您可以单击以使其具有焦点,但不能使用键盘上的Tab使其聚焦”。 tabindex="0"表示“您可以通过单击或使用Tab键将其聚焦,浏览器将通过Tab键选择不同元素聚焦的顺序。” 与<select>元素不同,弹出框将无法移出浏览器窗口(这可能是对所有用户定义内容的故意限制-如果用户定义内容可能会超出页面区域的边缘,网站可能会造成混淆或欺骗用户的安全风险。)

As a bonus, you’ll be able to make a drop-down list that is not a combo box with the dropdown class alone:

作为奖励,您将能够制作一个下拉列表, 该列表不是仅包含dropdown类的组合框:

<div class="dropdown">
   *** <span tabindex="0">Dropdown menu</span> *** 
   <div>
     Contents of drop-down popup go here
   </div>
</div>

This is intended as a click-to-open drop-down menu (if you want a drop-down menu that opens on mouse hover instead of mouse click, there are already many other tutorials about that.)

这旨在用作“单击打开”下拉菜单(如果您希望在鼠标悬停而不是单击鼠标时打开一个下拉菜单,则已经有许多其他教程 。)

In this case, the last element contains the drop-down content and all other children are always visible, but only elements with a tabindex attribute can be clicked to open the popup area.

在这种情况下,最后一个元素包含下拉内容,并且所有其他子元素始终可见,但是只能单击具有tabindex属性的元素以打开弹出区域。

You can safely edit the margin and border of a combo box and its children without messing up its behavior, except one thing: don’t let padding-right get too small because the ▾ down arrow is shown in the padding — its size should be at least 1em.

您可以安全地编辑组合框及其子元素的边距和边框,而不会弄乱其行为,只有一件事:不要让padding-right变得太小,因为padding中会显示▾向下箭头-它的大小应为至少1em

摘要 (Summary)

  • The combobox class is for a combo box

    combobox类用于组合框

  • The dropdown class is for menus and combo boxes that drop down when the top content is clicked (remember tabindex="0")

    dropdown类用于单击顶部内容时会下拉的菜单和组合框(请记住tabindex="0" )

  • The downarrow class adds the down-arrow icon (tabindex="-1" is required, because it cannot be added via CSS.)

    downarrow类添加了下箭头图标(因为不能通过CSS添加,所以必须使用tabindex="-1" 。)

  • The last child of combobox or dropdown is the dropdown content.

    comboboxdropdown的最后一个子项是下拉菜单内容。

And you can preview the demo with source code.

您可以预览带有源代码的演示

我们将需要CSS功能 (CSS Features We Will Need)

We’ll need a lot of stuff for this. Here’s a list (feel free to skip and read later.)

为此,我们将需要很多东西。 这是一个列表(随时可以跳过并稍后阅读。)

选择器 (Selectors)

Basic selectors: .a means “match elements with class='a'”.A, B means “match selector A or selector B”.A B means “match a B element that has an A element as an ancestor”.A > B meaning “match a B element whose parent is an A element”.

基本选择器: .a表示“匹配具有class='a'元素”。 A, B表示“匹配选择器A或选择器B ”。 AB意思是“匹配以A元素为祖先的B元素”。 A > B意思是“匹配其父元素为A元素的B元素”。

:first-child pseudo-selector:*:first-child means “match any element as long as it is the first child of some parent element”.

:first-child 伪选择器: *:first-child意思是“匹配任何元素,只要它是某个父元素的第一个孩子”即可。

:last-child pseudo-selector:*:last-child means “match any element as long as it is the last child of another element”. For example, .combobox > *:last-child finds the last child of any element with class="combobox".

:last-child伪选择器: *:last-child意思是“匹配任何元素,只要它是另一个元素的最后一个孩子”。 例如, .combobox > *:last-child找到class="combobox"元素的最后一个子元素。

:empty pseudo-selector:.downarrow:empty means “match an element with class="downarrow" if it doesn’t have anything in it (not even plain text)”.

:empty 伪选择器: .downarrow:empty意思是“如果元素中没有任何内容(甚至不是纯文本),则将其与class="downarrow" ”进行匹配”。

:only-child pseudo-selector:*:only-child means “match any element if it is the only child of some other element”.

:only-child 伪选择器: *:only-child意思是“匹配任何元素,如果它是其他元素的唯一子元素”。

:not pseudo-selector:.dropdown:not(.sticky) means “match an element with the dropdown class if it does not have the sticky class”.

:not 伪选择器: .dropdown:not(.sticky)意思是“如果元素不具有sticky类,则将其与dropdown类匹配”。

:focus pseudo-selector:.downarrow:focus means “match an element with the downarrow class if it has the focus because it has a tabindex and it was clicked with the mouse or selected with Tab”.

:focus 伪选择器: .downarrow:focus意思是“如果元素具有焦点,则将其与downarrow类进行匹配,因为它具有tabindex ,并且已用鼠标单击或使用Tab进行了选择”。

:hover pseudo-selector:.foo:hover means “match an element with the foo class when the mouse pointer is on top of it”.

:hover 伪选择器: .foo:hover意思是“当鼠标指针位于foo类之上时,将其与foo类进行匹配”。

A ~ B means “match B if an earlier sibling matched A”.

A ~ B表示“如果较早的同级匹配A则匹配B ”。

款式 (Styles)

Basic styles:Make sure you understand the box model and its various associated styles (including width, height, min-width and max-height) before you continue. You should also know about other basic styles like font-size, font-family, color, and background-color.

基本样式:在继续之前,请确保您了解盒子模型及其各种相关样式(包括widthheightmin-widthmax-height )。 您还应该了解其他基本样式,例如font-sizefont-familycolorbackground-color

You should also know about units, especially the most common units: px, em, rem, and %.

您还应该了解单位,尤其是最常见的单位pxemrem%

box-sizing: border-box styleThis means that the width and height of an element includes the padding and border.

box-sizing: border-box 样式这意味着元素的宽度和高度包括padding和border

display: styleWe’ll be using display: block, which displays an element as a “block”, which is like a paragraph in that two adjacent blocks have line breaks between them.

display: style我们将使用display: block ,它将元素显示为“块”,就像一个段落,因为两个相邻的块之间有换行符。

We will also use display: inline-block, which displays an element inline, like an icon image within a paragraph, but still allows margins, borders and padding.

我们还将使用display: inline-block ,它显示元素inline ,类似于段落中的图标图像,但仍然允许边距,边框和填充。

We will not explicitly use display: inline, which is used for elements that do not have margins, borders or padding and do not need line breaks between them (<b>like this</b>).

我们将不会显式使用display: inline ,它用于没有边距,边框或填充并且它们之间不需要换行的元素( <b>like this</b> )。

Learn more about display.

详细了解展示广告。

position: styleIn the combo box, we will see how this style is used to take elements out of the normal document flow.

position: 样式在组合框中,我们将看到如何使用此样式将元素从常规文档流中取出。

Elements normally have a style of position: static, which just means “position it on the page normally”.

元素通常具有一种position: static样式position: static ,仅表示“将其正常放置在页面上”。

position: relative is like static, except two things: first, the element can be shifted left, right, up or down without affecting any other elements.However, the combo box doesn’t need this feature. The second effect of relative is to mark the element as “positioned”.

position: relative就像static ,除了两件事:首先,可以在不影响任何其他元素的情况下向左,向右,向上或向下移动元素。但是,组合框不需要此功能。 relative的第二个作用是将元素标记为“已定位”。

This matters because another position, absolute, positions an element relative to its nearest “positioned” ancestor. Specifically, the drop-down popup will use position: absolute in order to position itself relative to the top part of the combo box — therefore the combo box itself is marked relative.

这很重要,因为另一个位置( absolute位置)相对于元素的最近“已定位”祖先定位元素。 具体来说,下拉菜单将使用position: absolute相对于组合框的顶部进行定位,因此组合框本身被标记为relative

Also, an absolute element doesn’t affect the positioning of other items on the page, not even its own parent element, and that’s just what we want for a popup box.

另外, absolute元素不会影响页面上其他项目的位置,甚至不会影响其自身的父元素,而这正是我们想要的弹出框。

left, top, right and bottom stylesThese styles are used with position: relative and position: absolute, and they work a little differently for each one. More on that later.

left toprightbottom样式这些样式与position: relativeposition: absolute ,并且每种样式的工作方式略有不同。 以后再说。

Learn more about positioning.

详细了解定位。

outline: styleOutline is an extra border drawn outside an element’s normal border. It is normally used to highlight an element, like to indicate it has been “selected” by a user. Because outlines are expected to be temporary, they don’t occupy space on the page — so adding an outline won’t push other elements out of the way.

outline: 样式 Outline是在元素的法线边界之外绘制的额外边界。 它通常用于突出显示元素,例如表示用户已对其进行“选择”。 由于轮廓是临时的,因此它们不会在页面上占据空间-因此添加轮廓不会妨碍其他元素。

box-shadow: styleDraws a shadow “under” the element (well, actually the shadow is drawn outside the element, which looks very strange if the element has no background). This will be handy for the drop-down popup!

box-shadow: 样式在元素下方绘制阴影(嗯,实际上阴影是在元素外部绘制的,如果元素没有背景,则看起来很奇怪)。 这对于下拉菜单非常方便!

z-index: styleThis style changes the order in which an element is drawn by the browser. A higher z-index causes an element to be drawn later so that it appears to be above other things on the page.

z-index: 样式此样式更改浏览器绘制元素的顺序。 较高的 z-index会导致元素稍后被绘制,从而使其看起来在页面上的其他位置之上。

We’ll need a large z-index for our drop-down popup so that it appears on top of everything else. The children of the popup will get a new “stacking context”, which basically means they will automatically be drawn on top of the popup, which is good.

我们需要一个较大的z-index作为下拉菜单,以便它显示在其他所有内容的顶部。 弹出窗口的子级将获得新的“堆叠上下文”,这基本上意味着它们将自动绘制在弹出窗口的顶部,这很好。

Caution: z-index only works on “positioned” elements.

注意z-index仅适用于“定位”元素。

cursor: style Controls the mouse cursor’s appearance.

cursor: 样式控制鼠标光标的外观

text-align: style Horizontal text justification (left, right or center).

text-align: 样式水平文本对齐方式 ( leftrightcenter )。

pointer-events: styleThis style’s none setting makes an element “invisible” to mouse clicks.

pointer-events: 样式此样式的none设置使元素对鼠标单击 “不可见”。

transform: styleAllows you to rotate, scale, skew, or translate a block (or inline-block) element. These transforms are smart and affect mouse input also.

transform: style允许您旋转,缩放,倾斜或平移块(或行内块)元素。 这些转换很聪明,并且也会影响鼠标输入。

For example, you could rotate text 30 degrees and still select it with the mouse.

例如,您可以将文本旋转30度,然后仍然使用鼠标选择它。

transition: styleEnables animation when styles change.

transition:样式更改样式时启用动画

opacity: styleA number between 0 and 1 controls how easy an element is to see: 1 is the normal value which makes an element fully visible0 makes an element completely invisible. (Unlike visibility: hidden and display: none, the other ways of making something invisible, opacity: 0 does not prevent the mouse from interacting with the element.)

opacity: 样式 0到1之间的数字控制元素的可见程度: 1是使元素完全可见的常规值0使元素完全不可见。 (与visibility: hiddendisplay: none不同,使某事物不可见, opacity: 0的其他方法是opacity: 0不会阻止鼠标与元素进行交互。)

In this article, we will use opacity for animation — by animating the transition between opacity: 0 and opacity: 1, we can make an element fade in or out.

在本文中,我们将不透明度用于动画制作-通过对opacity: 0opacity: 1之间的过渡进行动画处理,可以使元素淡入或淡出。

伪元素 (Pseudo-element)

::before or ::after: Refers to a virtual element within an element previously selected, before or after its normal content.

::before::after先前选择的元素中,在其常规内容之前或之后引用虚拟元素。

For example if you write p::before { content: "!" } then ! will appear at the beginning of every paragraph.

例如,如果您p::before { content: "!" }p::before { content: "!" } p::before { content: "!" }然后! 将出现在每个段落的开头。

We can use content with ::before or ::after to draw the down arrow (▾).

我们可以使用content::before::after得出的向下箭头(▾)。

准备初始外观 (Preparing the initial appearance)

.combobox and .dropdown need to be relative so that the drop-down popup can be positioned relative to them. display: inline-block allows the combo box to have margins, padding and border. Unlike display: block it allows other things to appear on the same line (such as labels or other combo boxes.)

.combobox.dropdown必须是relative以便下拉菜单可以相对于它们定位。 display: inline-block允许组合框具有边距,填充和边框。 与display: block不同,它允许其他内容显示在同一行(例如标签或其他组合框)。

.combobox, .dropdown { 
  /* "relative" and "inline-block" (or just "block") are needed
     here so that "absolute" works correctly in children */
  position: relative;
  display: inline-block;
}

Combo boxes, but not drop-down lists, will have a built-in border:

组合框(但没有下拉列表)将具有内置边框:

.combobox {
  border: 1px solid #999;
  padding-right: 1.25em; /* leave room for ▾ */
}

The color #999 is slightly darker than the border on Chrome’s <select> element, and slightly lighter than FireFox’s <select> element, so it doesn’t look too much different than either of them.

#999颜色比Chrome的<select>元素的边框略深,并且比FireFox的<select>元素略浅,因此看起来两者之间没有太大区别。

我们如何绘制小向下箭头(▾)? (How do we draw the little down arrow (▾)?)

The difficulty here is controlling its height. The combo box might have content of an unpredictable size: small font, large font, one line or two lines. The arrow “button” needs to have the same height so that it works no matter where the user clicks on it — anywhere within the border should work.

这里的困难是控制其高度。 组合框的内容可能无法预测:小字体,大字体,一行或两行。 箭头“按钮”必须具有相同的高度,以便无论用户在何处单击都可以使用它-边框内的任何地方都可以使用。

So, how can we make the arrow adapt to the height of its left sibling? CSS grid can accomplish this straightforwardly, but it is not supported by all browsers. Perhaps Flexbox could do the job too, but I decided to use an old trick for compatibility with older browsers: absolute positioning.

那么,如何使箭头适应左兄弟姐妹的高度? CSS网格可以直接完成此操作,但并非所有浏览器都支持。 也许Flexbox也可以做到这一点,但是我决定使用一个旧技巧来与旧版浏览器兼容:绝对定位。

With absolute positioning, I can force the arrow to have the same height as its container.

通过绝对定位,我可以强制箭头具有与其容器相同的高度。

The disadvantage of this approach is that the arrow will exist outside the normal flow of the document, so the browser won’t reserve any space for it. Instead, we will give the combo box some padding on the right side (1.25em above), and the arrow will live within the padding.

这种方法的缺点是箭头将存在于文档的常规流程之外,因此浏览器不会为其保留任何空间。 取而代之的是,我们将在组合框的右侧(上方1.25em )处留一些填充,并且箭头将位于填充内。

In absolute positioning mode, top aligns the top edge of an element relative to the top edge of its container: top: 0 means the two top edges will be at the same location. Similarly left: 0 aligns the left side of the element to the left side of the container, and so on.

在绝对定位模式下, top将元素的顶部边缘相对于其容器的顶部边缘对齐: top: 0表示两个顶部边缘将位于同一位置。 同样, left: 0将元素的左侧与容器的左侧对齐,依此类推。

Positive coordinates push the element “inward” relative to the container, so top: 10px means “put the top of the element 10px down from the top of the parent”, while bottom: 10px means “put the bottom of the element 10px up from the bottom of the parent.”

正坐标将元素相对于容器“向内”推入,因此top: 10px表示“将元素的顶部从父级的顶部向下放置10px”,而bottom: 10px表示“将元素的底部从容器的top: 10px放置bottom: 10px ”父母的最底层。”

In this case we need top: 0; bottom: 0; right: 0; width: 1.25em to put the arrow on the right side, top-to-bottom.

在这种情况下,我们需要top: 0; bottom: 0; right: 0; width: 1.25em top: 0; bottom: 0; right: 0; width: 1.25em top: 0; bottom: 0; right: 0; width: 1.25em ,将箭头top: 0; bottom: 0; right: 0; width: 1.25em放在右侧。

.combobox > .downarrow, .dropdown > .downarrow {
  display: block;     /* Allow margin/border/padding/size */
  position: absolute; /* Outside normal flow */
  top: 0;    /* Align top of downarrow with top edge of combobox */
  bottom: 0; /* Align bottom of downarrow with bottom of combobox */
  right: 0; /*Align right edge of downarrow with right of combobox*/
  width: 1.25em;
  
  cursor: default; /* Use arrow cursor instead of I-beam */
  nav-index: -1; /* sets tabindex, nonfunctional in most browsers */
  border-width: 0;        /* disable by default */
  border-color: inherit;  /* copy parent border */
  border-left: inherit;   /* copy parent border */
}

Here, display: block and display: inline-block have the same effect, so I used the shorter one. I also disabled the I-beam mouse cursor normally shown over text (since the down arrow counts as text).

在这里, display: blockdisplay: inline-block具有相同的效果,因此我使用了较短的效果。 我还禁用了通常显示在文本上方的I型鼠标光标(因为向下箭头算作文本)。

There is actually a way to set tabindex in CSS, it’s called nav-index. But most browsers don’t support it, so if you find that your combo box only works in Opera, you know why.

实际上,有一种方法可以在CSS中设置tabindex ,即nav-index 。 但是大多数浏览器都不支持它,因此,如果您发现组合框仅在Opera中可用,您就会知道原因。

You must therefore add tabindex="-1" beside class="downarrow".

因此,您必须在class="downarrow"旁边添加tabindex="-1" class="downarrow"

This code disables the borders, with the caveat that the border color/style should be inherited from the parent element (the combo box) if other CSS increases border-left-width. You can use the inherit option on any attribute that doesn’t inherit from parent by default, by the way.

此代码禁用了边框,并告诫,如果其他CSS增加border-left-width ,则边框颜色/样式应从父元素(组合框)继承。 顺便说一下,您可以对默认情况下不从父级继承的任何属性使用“ inherit选项。

I decided there should be a left border if the popup won’t open when the left side is clicked. That way, the drop-down arrow looks like a button, subtly suggesting it can be clicked. Remember the plan: only dropdown, not combobox alone, will open when the left side is focused.

我决定如果单击左侧时弹出窗口无法打开,则应该有一个左边框。 这样,下拉箭头看起来就像一个按钮,巧妙地暗示可以单击它。 请记住该计划:只有左侧dropdown时才会打开dropdown ,而不是单独的combobox

Therefore I will add a border when combobox alone is used:

因此,当单独使用combobox时,我将添加一个边框:

.combobox:not(.dropdown) > .downarrow {
  border-left-width: 1px;
}

Next, if the user has provided us with an empty <span class="downarrow"></span>, we need to magically add the missing down arrow character using ::before (or ::after) and content:

接下来,如果用户为我们提供了一个空的<span class="downarrow"></span> ,我们需要使用::before (或::after )和content来神奇地添加缺少的向下箭头字符:

.downarrow:empty::before {
  content: '▾';
}

The down arrow needs to be centered within the .downarrow element, too. text-align: center will center the text horizontally, but vertical centering is tricky. vertical-align: middle doesn’t work, because it is designed to align inline elements with the surrounding text. What we want is to align our down arrow pseudo-element with the parent .downarrow container.

向下箭头也需要在.downarrow元素内居中。 text-align: center将文本水平居中,但垂直居中则比较棘手。 vertical-align: middle不起作用,因为它旨在使内联元素与周围的text对齐。 我们想要的是将向下箭头的伪元素与 .downarrow容器.downarrow

There’s a trick to it:

有一个窍门:

.downarrow::before, .downarrow > *:only-child {
  text-align: center; /* Center horizontally */
  /* vertical centering trick */
  position: relative; /* Allow the element to be shifted */
  top: 50%;           /* Move down by 50% of container size */
  transform: translateY(-50%); /* Move up by 50% of element size */
  display: block;     /* `transform` requires block/inline-block */
}

Remember that we add the ::before content only if the .downarrow is empty. If the user has provided their own custom down arrow element, we still want to center it, hence the .downarrow > *:only-child selector.

请记住,仅当.downarrow为空时,才添加::before内容。 如果用户提供了他们自己的自定义向下箭头元素,我们仍然希望将其居中,因此.downarrow > *:only-child选择器。

And if the combo box contains an <input> element, it shouldn’t have a border:

而且,如果组合框包含<input>元素,则不应有边框:

.combobox > input {
  border: 0 /* combo box already has a border */
}

This next part is optional, but usually the first child of a combo box should have a width of 100% of its parent .combobox so that if the combo box is wider than its first child, the first child stretches to match. And in case the user constructed the combo box out of spans rather than divs (perhaps so it could be placed within a <p>), it may make sense to set the first child as inline-block so it can have padding and margins.

下一部分是可选的,但是通常,组合框的第一个子.combobox的宽度应为其父.combobox的100%,这样,如果组合框比其第一个子.combobox宽,则第一个子.combobox延伸以匹配。 并且如果用户在跨度而不是div的范围内构造了组合框(也许可以将其放置在<p> ),则可以将第一个子级设置为inline-block这样它可以具有填充和边距。

.combobox > *:first-child {
  width: 100%;
  box-sizing: border-box; /* so 100% includes border & padding */
  display: inline-block;
}

准备下拉列表 (Preparing the drop-down list)

Initially we just want it hidden, so we can use display: none.

最初,我们只是希望它隐藏,所以我们可以使用display: none

But in preparation for when it is visible, let’s set some other properties too. Start with position: absolute so it’s outside the normal document flow (remember that an absolute element is positioned relative to its nearest relative ancestor, which is .combobox or .dropdown). When displayed, it should have a border and a background, of course, and also a shadow underneath it.

但是在准备何时可见时,我们还要设置其他一些属性。 从position: absolute开始position: absolute因此它不在常规文档流中(请记住, absolute元素相对于其最接近的relative祖先( .combobox.dropdown )定位。 当显示时,它当然应该有边框和背景,并在其下面还带有阴影。

Here you see box-shadow: 1px 2px 4px 1px #4448, which means “show a shadow 1px to the right of the element, 2px downward, blurred by 4px, and make the shadow 1px larger than the element itself, with a color of #4448”. We also need a nice big z-index so the popup will appear on top of everything else:

在这里,您可以看到box-shadow: 1px 2px 4px 1px #4448 ,这意味着“在元素右侧显示一个阴影1px,向下显示2px,被4px模糊,并使阴影比元素本身大1px,颜色为#4448”。 我们还需要一个不错的大z索引,以便弹出窗口会显示在其他所有内容的顶部:

.dropdown > *:last-child,
.combobox > *:last-child {
  display: none;          /* hidden by default */
  position: absolute;     /* outside document flow */
  left: 0;          /* Left side of popup = left side of parent */
  top: 100%;        /* Top of popup = 100% below top of parent */
  border: 1px solid #999; /* gray border */
  background-color: #fff; /* white background */
  box-shadow: 1px 2px 4px 1px #4448; /* shadow behind */
  z-index: 9999;          /* draw on top of everything else */
  min-width: 100%;        /* >= 100% as wide as its container */
  box-sizing: border-box; /* width includes border & padding */
}

Here I’ve used left: 0 and top: 100% to position the popup correctly, but in this case it turns out that the default position of the popup is practically the same, so these styles aren’t really necessary.

在这里,我使用left: 0top: 100%来正确定位弹出窗口,但是在这种情况下,事实证明弹出窗口的默认位置实际上是相同的,因此这些样式并不是必需的。

To make the drop-down box visible, all we really need is display: block.

为了使下拉框可见,我们真正需要的是display: block

But which selectors do we need to make that happen?

但是我们需要哪些选择器来实现呢?

??? {
  display: block;
}

Most obviously, the drop-down should be shown in these three cases.

最明显的是,在这三种情况下都应显示下拉列表。

  1. The user clicked the .downarrow

    用户单击.downarrow

  2. The user clicked or tabbed to .dropdown

    用户单击或选项卡到.dropdown

  3. The user clicked or tabbed to a child of .dropdown

    用户单击或选项卡到.dropdown的子.dropdown

The drop-down box is the last child, so we’ll need to combine the *:last-child selector with :focus to detect when one of the above things has been clicked or tabbed-to:

下拉框是最后一个子项,因此我们需要将*:last-child选择器与:focus以检测是否单击了以下其中一项或将它们选项卡了:

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus              > *:last-child,
.dropdown > *:focus          ~ *:last-child {
  display: block;
}

We’re not done yet, though. What if the user clicks a text box or a link inside the drop-down box? The click will cause the .downarrow or the .dropdown to lose the focus, causing the drop-down box to disappear instantly.

不过,我们还没有完成。 如果用户单击文本框或下拉框中的链接怎么办? 单击将导致.downarrow.dropdown失去焦点,导致下拉框立即消失。

In the case of a link, the browser focuses the link when the mouse button goes down but it does not follow the link until the mouse button is released. So if the drop-down disappears instantly, any links in the drop-down cannot be followed!

对于链接,浏览器会在鼠标按钮按下时将焦点放在该链接上,但是直到释放鼠标按钮时,浏览器才会关注该链接。 因此,如果该下拉列表立即消失,则该下拉列表中的任何链接都无法被跟踪!

To fix this, we should keep the box open whenever something within the :last-child has the focus:

为了解决这个问题,只要在:last-child中的某个焦点出现时,我们应该保持框打开:

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > *:last-child:focus-within,
.dropdown > *:last-child:focus-within {
  display: block;
}

Caution: This doesn’t work in Edge/IE (a workaround is described below).

警告:这在Edge / IE中不起作用(解决方法如下所述)。

If the down-arrow is clicked a second time, we should hide the drop-down box. This can be accomplished like so:

如果再次单击向下箭头,则应隐藏下拉框。 可以这样完成:

.downarrow:focus {
  pointer-events: none; /* Causes second click to close */
}

This causes the .downarrow to be invisible to mouse events when it has the focus, so that when you click it, you are actually clicking what is behind it (the .combobox). This causes it to lose the focus, which in turn causes the drop-down box to disappear.

这会导致.downarrow在具有焦点时对鼠标事件不可见,因此单击它时,实际上是在单击它后面的内容( .combobox )。 这将导致其失去焦点,从而使下拉框消失。

We can do the same thing for .dropdown, so clicking the top area of a .dropdown again makes it disappear:

我们可以做同样的事情.dropdown ,所以点击顶部区域.dropdown再次使它消失:

.dropdown > *:not(:last-child):focus,
.downarrow:focus,
.dropdown:focus {
  pointer-events: none; /* Causes second click to close */
}

This mostly works. But if your top area contains a text box, there is a side effect since the text box won’t process mouse input normally. However, I have found that the text box is still usable.

这大多有效。 但是,如果您的顶部区域包含一个文本框,则会产生副作用,因为该文本框将无法正常处理鼠标输入。 但是,我发现该文本框仍然可用。

In Firefox you can click and drag to select text if you start when the popup is closed, but it doesn’t work when the popup is open. In Edge it’s the opposite: you can click and drag to select text only when the popup is open. Either way, it’s basically usable since the user is likely to retry once if his input doesn’t work the first time.

在Firefox中,如果您在关闭弹出式窗口时启动,则可以单击并拖动以选择文本,但是在弹出式窗口打开时不起作用。 在Edge中则相反:只有在弹出窗口打开时,您才能单击并拖动以选择文本。 无论哪种方式,它基本上都是可用的,因为如果用户的输入第一次不起作用,则用户可能会重试一次。

Chrome’s behavior is… inconsistent. In any case, to get perfect behavior — where a click closes the box without causing the text box to lose focus — I think JavaScript is required.

Chrome的行为是……不一致。 无论如何,要获得完美的行为(单击即可关闭框而不会导致文本框失去焦点),我认为JavaScript是必需的。

画龙点睛 (Finishing touches)

The combo box should normally have a margin. But this seems optional, since <input> controls don’t have one by default:

组合框通常应具有边距。 但这似乎是可选的,因为默认情况下<input>控件没有一个:

.combobox {
  margin: 5px;
}

Let’s make this thing cooler by opening the box with animation.

让我们通过打开带有动画的框来使其更酷。

The transition property is the easiest way to do animations. In fact, for our purposes, a simple command like transition: 0.4s; enables animations for all supported styles. But so far the only style we are changing is display, and changes to display cannot be animated.

transition属性是制作动画的最简单方法。 实际上,就我们的目的而言,有一个简单的命令,例如transition: 0.4s; 为所有支持的样式启用动画。 但是到目前为止,我们要更改的唯一样式是display ,并且无法对display更改进行动画处理。

So let’s try animating a transition from opacity: 0 to opacity: 1 by modifying our existing styles…

因此,让我们尝试通过修改现有样式来动画化从不opacity: 0opacity: 1的过渡…

.dropdown > *:last-child,
.combobox > *:last-child {
  display: none;
  /* 
     ... other styles same as before ...
  */
  opacity: 0;
  transition: 0.4s;
}

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > *:last-child:focus-within,
.dropdown > *:last-child:focus-within {
  display: block;
  opacity: 1;
  transition: 0.15s;
}

The time on the transition controls how long it takes to enter the current state. So this code should mean “take 0.15 seconds to show and 0.4 seconds to hide.”

过渡时间控制进入当前状态的时间。 因此,此代码应表示“ 显示时间为0.15秒, 隐藏时间为0.4秒”。

But the animation doesn’t work. It turns out that display: hidden blocks animations. Instead we need to use one of the other ways of hiding things. Another way to hide things is with visibility: hidden. Unfortunately, this partially blocks animations, too — the animation for showing the popup works, but the animation for hiding the popup doesn’t.

但是动画不起作用事实证明display: hidden阻止动画。 相反,我们需要使用其他隐藏方式之一。 隐藏事物的另一种方法是具有visibility: hidden 。 不幸的是,这也部分地阻止了动画-用于显示弹出窗口的动画有效,但是用于隐藏弹出窗口的动画无效。

We can’t rely on opacity: 0 by itself to hide an element, because the mouse can still interact with an element that has opacity: 0. However, we can fix this with pointer-events: none.

我们不能依靠opacity: 0 本身来隐藏元素,因为鼠标仍然可以与具有opacity: 0的元素进行交互。 但是,我们可以使用pointer-events: none来解决此问题pointer-events: none

So the working fade-in and fade-out looks like this:

因此,工作的淡入和淡出看起来像这样:

.dropdown > *:last-child,
.combobox > *:last-child {
  display: block;
  /* 
     ... other styles same as before ...
  */
  transition: 0.4s;
  opacity: 0;
  pointer-events: none;
}

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > *:last-child:focus-within,
.dropdown > *:last-child:focus-within {
  display: block;
  transition: 0.15s;
  opacity: 1;
  pointer-events: auto;
}

Another flourish we could add is to move the popup into position, like by animating top:

我们可以添加的另一个功能是将弹出窗口移动到适当位置,例如通过对top动画:

.dropdown > *:last-child,
.combobox > *:last-child {
  display: block;
  /* 
     ... other styles same as before ...
  */
  top: 0;
  opacity: 0;
  transition: 0.4s;
  pointer-events: none;
}

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > *:last-child:focus-within,
.dropdown > *:last-child:focus-within {
  display: block;
  top: 100%;
  opacity: 1;
  transition: 0.15s;
  pointer-events: auto;
}

I decided this was a bit “over the top” and did not include it in the final version.

我认为这有点“过头了”,没有在最终版本中包括它。

Finally, we should have a focus rectangle — a border showing when the combo box is “active”.

最后,我们应该有一个焦点矩形-组合框处于“活动”状态时显示的边框。

First let’s add a focus rectangle for that down arrow:

首先,我们为该向下箭头添加一个焦点矩形:

.downarrow:focus {
  outline: 2px solid #48F8;
}

Ideally we would have a focus rectangle for the combo box itself, like this:

理想情况下,组合框本身将具有一个焦点矩形,如下所示:

.combobox:focus-within {
  outline: 2px solid #48F;
}

This works fine in Chrome. But in Firefox 61 the outline is expanded beyond the border to enclose the entire popup box also, which looks a little odd, especially if the popup box doesn’t have the same width as the top part. In Edge the outline doesn’t show up at all because Edge doesn’t support :focus-within (see below). So, what can we do instead?

在Chrome中可以正常使用。 但是在Firefox 61中, outline线已扩展到边框之外,以包围整个弹出框,这看起来有些奇怪,尤其是当弹出框的宽度与顶部宽度不同时。 在Edge中,轮廓根本不显示,因为Edge不支持:focus-within (请参见下文)。 那么,我们该怎么办呢?

I decided to use this:

我决定使用此:

.combobox > *:not(:last-child):focus {
  outline: 2px solid #48F8;
}

This draws an outline around the focused child instead of the combo box itself. But this sometimes looks odd too, if the child is not the same size as the enclosing combo box. So I added transparency (#48F8 instead of #48F) to make it less visible, and therefore less odd-looking in the worst case.

这将围绕关注的孩子而不是组合框本身绘制轮廓。 但是,如果孩子的大小与封闭的组合框的大小不同,有时这看起来也很奇怪。 因此,我添加了透明度( #48F8而不是#48F ),以使其不那么明显,因此在最坏的情况下外观也不那么奇怪。

粘性 (Stickiness)

The styles we have so far keep the box open only when something is focused. So if you click on plain text in the popup area, the popup closes. For the final version I expanded the list of reasons to keep the popup open to include a sticky style that will keep the drop-down open on mouse hover, so that clicking doesn’t close the box

到目前为止,我们拥有的样式仅在焦点对准时才打开框。 因此,如果您在弹出区域中单击纯文本,则弹出窗口将关闭。 对于最终版本,我扩展了使弹出窗口保持打开状态的原因列表,其中包括一种sticky样式,该样式将在鼠标悬停时使下拉菜单保持打开状态,以便单击不会关闭框

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *:focus ~ *:last-child,
.combobox > *:last-child:focus-within,
.dropdown > *:last-child:focus-within,
.combobox > .sticky:last-child:hover,
.dropdown > .sticky:last-child:hover {
  display: block;
  top: 100%;
  opacity: 1;
  transition: 0.15s;
  pointer-events: auto;
}

As I discussed earlier, glitches occur when the top area of a combo box contains a text box. To let you easily avoid this problem, I tweaked the existing CSS so that the pointer-events: none style is not applied if the .dropdown element also has the sticky class:

正如我之前所讨论的,当组合框的顶部区域包含一个文本框时,就会出现故障。 为了让您轻松避免此问题,我对现有CSS进行了调整,以使pointer-events: none如果.dropdown元素也具有sticky类, 则不应用pointer-events: none样式:

.dropdown:not(.sticky) > *:not(:last-child):focus,
.downarrow:focus,
.dropdown:focus {
  pointer-events: none; /* Causes second click to close */
}

Finally, if a .dropdown list contains links, there is a small inconvenience. After clicking a link, the list will not close automatically since the link has the focus and we programmed the drop-down not to close when a child has the focus.

最后,如果.dropdown列表中包含链接,将带来一些不便。 单击链接后,列表将不会自动关闭,因为链接具有焦点,并且我们对下拉列表进行了编程,以使当孩子获得焦点时不会关闭。

To avoid this I added support for a new less-sticky class. Like sticky, less-sticky keeps the popup open when the mouse hovers over it. Unlike sticky, less-sticky does not keep the popup open when a child has the focus.

为了避免这种情况,我增加了对新less-sticky类的支持。 像sticky一样,当鼠标悬停在上面时, less-sticky会使弹出窗口保持打开状态。 与sticky不同,当孩子获得焦点时, less-sticky不会使弹出窗口保持打开状态。

So our new list of selectors is getting pretty long:

因此,我们的新选择器列表越来越长:

.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *: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-within:not(.less-sticky),
.dropdown > *:last-child:focus-within:not(.less-sticky) {
  display: block;
  opacity: 1;
  transition: 0.15s;
  pointer-events: auto;
  top: 100%;
}

And we’re not even done yet, because this is not compatible with Edge and Internet Explorer yet.

而且我们还没有完成,因为这还不兼容Edge和Internet Explorer。

边缘案例 (Edge Cases)

Once I got my combo box working perfectly in Firefox and Chrome, I was dismayed to see it completely ugly and unusable in Edge. What went wrong?

一旦我的组合框在Firefox和Chrome中完美运行,我很沮丧地看到它在Edge中非常难看且无法使用。 什么地方出了错?

First, the borders were gone because Edge and IE don’t support opacity on borders, as in rgb(200,150,100,50) or #8888. I had used #8888 as the border. To make it work on Edge, I changed it to #999.

首先,边界消失了,因为Edge和IE不支持边界的不透明度,例如rgb(200,150,100,50)#8888 。 我曾经用#8888作为边界。 为了在Edge上运行,我将其更改为#999

Another alternative is to offer a non-opaque border just for Edge:

另一种选择是仅为Edge提供不透明的边框:

border: 1px solid #888;  /* Edge/IE can't do border opacity */
border: 1px solid #8888; /* All other browsers */

Second, click as I might — the down-dropping-divs just wouldn’t drop down!

第二,按我可能的方式单击-下拉div不会下拉!

In solving this issue, I learned something new — if a browser doesn’t understand a selector used in a CSS declaration, it will ignore the entire block.

在解决此问题时,我学到了新的东西-如果浏览器不理解CSS声明中使用的选择器,它将忽略整个block

For instance if you write .x, .y, .z:unknown { margin:1em }, then x and y won’t get margins simply because the browser doesn’t understand unknown.

例如,如果您编写.x, .y, .z:unknown { margin:1em } ,则xy不会仅仅因为浏览器无法理解unknown而获得页边距。

It turned out that Edge doesn’t understand :focus-within, which is what allows the drop-down area to stay open when an input element deep within the drop-down area gets clicked. The problem was, I’d mixed supported and unsupported selectors together.

事实证明,Edge无法理解:focus-within ,当单击位于下拉区域深处的input元素时,该属性使下拉区域保持打开状态。 问题是,我将支持和不支持的选择器混合在一起。

In order to make Edge work at all, I needed to repeat the whole block of “how-to-open-the-drop-down-list” styles separately for the selectors that use :focus-within, so that those selectors don’t stop the other selectors from working.

为了使Edge正常工作,我需要为使用:focus-within的选择器分别重复整个“如何打开下拉列表”样式的块,以便这些选择器不t停止其他选择器的工作。

Then, as a workaround for the lack of :focus-within, I decided to attempt to detect Edge and automatically keep any .dropdown list open when the mouse is :hovering in that case. That way, it is still possible to use a focused element (such as an a href or an input) inside the drop-down area, although it will disappear early if the mouse moves off it.

然后,作为缺乏:focus-within的解决方法,我决定尝试检测Edge并在这种情况下将鼠标:hover时自动将任何.dropdown列表保持打开状态。 这样,尽管鼠标移开它,它仍会在早期消失,但仍可以在下拉区域内使用集中的元素(例如a hrefinput )。

The code for all this is as follows:

所有这些的代码如下:

/* List of situations in which to show the dropdown list. */
.combobox > .downarrow:focus ~ *:last-child,
.dropdown:focus > *:last-child,
.dropdown > *: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;
  }
}

Third, the outline style wasn’t working in Edge. Once again the problem was that Edge doesn’t support non-opaque outlines.

第三, outline样式在Edge中不起作用。 再次出现的问题是Edge不支持非透明轮廓。

The solution is a special opaque style for Edge:

解决方案是Edge的特殊不透明样式:

outline: 2px solid #8AF; /* Edge/IE can't do outline transparency */  
outline: 2px solid #48F8;

Fourth, I had placed two combo boxes within a <label> element, and attempting to open the second one always opens the first one instead. It turns out that in Edge, if you are using a mouse, you can only select the first input element within a label.

第四,我在<label>元素中放置了两个组合框,尝试打开第二个组合框总是打开第一个组合框。 事实证明,在Edge中,如果使用鼠标,则只能选择标签中的第一个输入元素。

Fifth, the dropdown boxes didn’t have shadows. Once again this was because I used a non-opaque shadow, and once again Edge needed its own special CSS:

第五,下拉框没有阴影。 再次是因为我使用了不透明的阴影,并且Edge再一次需要自己的特殊CSS:

box-shadow: 1px 2px 4px 1px #666; /* Edge can't do shadow opacity */
box-shadow: 1px 2px 4px 1px #4448;

Internet Explorer 11 has almost exactly the same limitations, so fixing Edge mostly fixed IE, except that a different browser detection technique was needed for IE than Edge.

Internet Explorer 11具有几乎完全相同的局限性,因此修复Edge主要是修复IE,除了IE需要与Edge不同的浏览器检测技术。

将弹出窗口与顶部区域同步 (Synchronizing the popup with the top area)

Unfortunately, CSS can’t do this for us. So in the final demo, JavaScript is used to update the top part of the combo box when the popup part changes. For instance, I used this jQuery-based code to update the top part of the color picker:

不幸的是,CSS无法为我们做到这一点。 因此,在最终的演示中,当弹出部分更改时,使用JavaScript来更新组合框的顶部。 例如,我使用了以下基于jQuery的代码来更新颜色选择器的顶部:

function parentComboBox(el) {
  for (el = el.parentNode; el && 
    Array.prototype.indexOf.call(el.classList, "combobox") <= -1;)
    el = el.parentNode;
  return el;
}
$(".combobox .color").mousedown(function() {
  var c = this.style.backgroundColor;
  $(parentComboBox(this)).find(".color")[0].
    style.backgroundColor = c;
});

最终版本 (Final version)

Click here to view the demo with source code on CodePen.

单击此处在CodePen上查看带有源代码的演示。

翻译自: https://www.freecodecamp.org/news/mostly-css-drop-down-combo-boxes-4ff4bb182ff7/

css实现列表下拉菜单

    原文作者:cumi6497
    原文地址: https://blog.csdn.net/cumi6497/article/details/108156982
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞