I (and, as it seems to me, many of you) encountered more than once the incompatibility of selects with the design of the site. The pain is that they can not be stylized, and in each browser, they look their own way.
Of course, there are a huge number of solutions provided by frameworks / libraries (the same bootstrap). But they all suggest the presence of JS. Of course, there is nothing scary / bad about this, but I tried to make a styled select without JS as a fallback in case js breaks for some reason.
Under the tools in this context, I mean what we will replace the select. And my choice fell on the radio buttons because of their similar behavior: you can choose only one option at a time.
<div class="options"> <label> <input type="radio" name="r" value="111" checked> <div class="value">11text11</div> </label> ....
We wrapped radio buttons in <label /> to avoid unnecessary IDs and for-s.
To implement the selection of the desired item as in the select, we need it to be in the top position of the list. To do this, we denote that the value of the selected element must be positioned absolutely (to break out of the stream) and be placed on the very top:
#dropdown .options :checked + .value{ position: absolute; top: 0; }
We also need to follow the rule: the height of each item must be equal to the "vacant" place (indent from above) for the selected item
#dropdown .options .value{ height: var(--item-height); } #dropdown .options{ padding-top: var(--item-height); }
The main part of the work - when you select the item, he is in a leading position. It remains to ensure that in the "calm" state only the selected item is visible, and the rest of the list is expanded when clicked, and when clicked to "milk" the list should be closed without changing the value.
Pseudo-selector manipulation comes to mind : focus . Add some input that will fall under it. I chose [type = text] because it can be sized (stretched to full width and height) and shielded by the leading selected element.
<div class="dropdown" id="dropdown"> <input type="text"> <div class="options"> ....
Hide the drop-down list will be height restriction and overflow: hidden :
#dropdown .options{ padding-top: var(--item-height); overflow: hidden; height: 0; } #dropdown > :focus + .options{ height: var(--list-height); }
Of course, the input must not be visible (like the radio buttons that represent the options):
#dropdown input{ opacity: 0; }
Opacity should be used : 0; instead of display: none; due to the fact that the hidden elements ( visually: hidden including) cannot have the state : focus .
And when it already seemed that everything turned out, I was surprised: when I clicked on a menu item from the list, the focus from the managing director leaves faster than clicking on the list item. That is, exactly the same thing happens when you click into the milk - the list just closes.
To increase the delay to hide the drop-down list, you have to use the dirty hack:
#dropdown .options{ ... transition: 0s .1s height; }
Done, see an example (I added some styles for beauty): https://jsfiddle.net/2k1pvbyt/
If we go further, we will want to make a multi-select in the same way (without JS). Not every integrator of jquery plug-ins will do this with JS (JQuery), but we have thought of something with you! Well said - done, you can not fall face in the dirt. Let's try to figure out if this is possible. And, if not, that at what point exactly it is impossible to do without js.
What do we need to change in the previous example in order for our select to become multi? Each item should be able to be selected regardless of the others. Obviously, we need to change the radio buttons for checkboxes:
<label data-value="111"> <input type="checkbox" required> </label>
Yes, that's not all, and it is not required by chance, with its help we will manipulate our list.
<fieldset> <label data-value="111"> <input type="checkbox" required> </label> </fieldset>
If we wrap it in <fieldset /> , then we will be able to manipulate pseudo selectors : valid / : invalid .
<fieldset/>, <form/> :valid , :valid
To understand what we should do, we need to clearly formulate the task:
Let the highlighted item be at the top of the list. On the same line with him other selected items.
Placing the selected element at the top of the list is easy, we will set the parent to display: flex and play with the value of order :
#dropdown .options > fieldset:invalid{ order: 2; } #dropdown .options{ display: flex; flex-wrap: wrap; } #dropdown fieldset{ flex-basis: 100%; }
The first condition is fulfilled and, in order to place all the selected items on one line, for the selected items we will specify:
#dropdown .options > fieldset:valid{ flex-basis: 10%; z-index: 3; }
Voila! looks like it works.
We could not figure out where (as part of the task) we can not do without js.
Perhaps (exactly), on more complex examples it will be so.
Duplicate links to examples:
Well, for those who do not want to write this "bunch of markup" with their hands, put up a script that will do it for you.
Add-ons and other issues are welcome, no doubt!
I came up with the idea to use the manager input. It is useful if we move away from the nojs concept, we will use a certain js-constructor for initialization.
Among the changes:
Source: https://habr.com/ru/post/313958/
All Articles