Imagine the following scenario: You, a website developer, are given an interesting design for a website to develop. While feature-rich, you notice the forms used contain custom designed select fields. Most form fields are globally known in the web development field for not adhering fully to style rules, likely due to their development made to conform to native design. As such, a dropdown arrow will look different in each Operating System. No biggie! Just whip up a custom component which looks like a dropdown field, place it on top of the
select tag, ensuring it is hidden, and bind functions to open it when the custom component’s arrow is clicked. Easy!
While the above is an excellent solution, and there are several examples online explaining how to render this custom field, depending on the usage of your dropdown list by users and devices, it may likely not be needed as the display of its items (the
option tags) may be better appreciated in a mobile setting, in which native display may override any given style, all in the name of facilitating the user to perform a selection.
If we are to take a page from what website builders that bloat a page’s code for the sake of added functionality, we could assume in a thought exercise each DOM element is draggable and able to be positioned anywhere on the screen without breaking layout. As such, it could be understandably easy to create one, if we’re able to position the elements (the dropdown, and the arrow) we want to style on top of each other, much in the way that happens with custom select fields.
Thanks to newer versions of CSS, this is actually possible. For one, an element’s position in the DOM can be easily modified thanks to the
position property. As long as we have an absolute element, either inside the page body or a parent element whose position is
relative, we are able to modify its position to place it anywhere we want.
Even without positioning, it is also possible to modify the position of an element thanks to the
transform property. This allows us to move an element from its position horizontally, vertically, or both, by specifying arguments of translation which may be measured units (like pixels), percentage units, or viewport units.
pointer-events, allows us to assign it an element, which will allow for any mouse events on it to “pass through” so that any element rendered below it experiences it.
To make the picture above a reality, we need code for both HTML and CSS. In HTML, the idea will be to use as few elements as possible in order to render the dropdown:
Before I explain the HTML, let’s see the CSS:
So now that both are available, I’ll explain:
- Although not necessary, a container, noted by the div with id
select-container, is placed, as a wrapper for the dropdown and arrow image. It is written as an added security to ensure absolute elements do not escape the boundaries of the dropdown list. Note how in CSS it has a
- The select tag, noted by id
dropdown, will encompass the width and height given to its parent, the container, as well as some styling to change its font color, background color, border color and radius, and padding. The property
outlineis also specified and set to none so that if the list is clicked, the outline provided by the browser doesn’t surround the element without accounting for the border radius:
The select tag also has a
cursor property, but I’ll discuss that one later.
- The options, or list elements, have been placed here for purposes of the example, and thus do not limit this technique in any way.
- Lastly I have included an image which has all of the styling of the arrow from the “design”, included in it, but it’s possible to render your own arrow fully with CSS, as I only included it here for simplicity.
Note how the properties specified are being used! Because the container has a relative position and the image has an absolute position, any change in position made to the arrow image will restrain it to the coordinates of the container. The
transform property is then used, to move the arrow image its whole width to the left, thereby placing it on the right edge of the
select tag, right on top of the unruly arrow.
pointer-events: none works like a charm then to let the
select arrow receive the clicks. With all the code provided, we should be able to get a custom dropdown list like this:
Also note that, while it may be possible to get this by using
display: flex on the container, thereby removing both position attributes, the
select element will adjust to 100% of the container’s width, minus the width given to the arrow image, but the idea is to have a dropdown list whose width matches the one of the container.
Sadly, because this is a trick that depends on the use of a native HTML element, it is not possible to edit the
option elements’ width. So this custom dropdown will not look as pretty if your custom element has rounded corners:
Also, because there are no pointer events for the arrow image, nothing happens to the mouse pointer as it enters the arrow image. This is why the
select tag has a
cursor: pointer property, to simulate recognition of the mouse over the image. This is no native behavior, but it provides some form of interactivity in a desktop environment.
But, if you’re targeting a mobile audience, then native list selection should take over, and the regular desktop option items would not be visible, nor would there be a need for cursor changes.
Although it will always be better to understand the functionality of a custom-made dropdown list, having the alternative to create a decent looking one with the tools natively provided never hurts.